{
  "schema": "aka-gst.course/1",
  "id": "qa-local-agent",
  "stage": 2,
  "language": "ru",
  "title": "Локальные AI-агенты, Git и DevOps",
  "subtitle": "Windows + macOS | Codex + Hermes | Ollama + Open-LLM-VTuber",
  "generated_at": "2026-08-13",
  "source": {
    "repository": "https://github.com/aka-gst/agent-lab",
    "document": "docs/praktikum_codex_hermes_full_windows_macos_ru_v3.docx",
    "downloads": [
      {
        "label": "PDF",
        "url": "https://github.com/aka-gst/agent-lab/raw/main/docs/praktikum_codex_hermes_full_windows_macos_ru_v3.pdf"
      },
      {
        "label": "DOCX",
        "url": "https://github.com/aka-gst/agent-lab/raw/main/docs/praktikum_codex_hermes_full_windows_macos_ru_v3.docx"
      }
    ]
  },
  "units": [
    {
      "id": "setup",
      "title": "Часть I. Самостоятельная настройка с нуля",
      "kind": "guide",
      "order": 1,
      "sections": [
        {
          "id": "section-1",
          "title": "",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Назначение. </strong>Эта часть не требует предыдущего практикума. Она ведёт от чистой Windows- или macOS-системы до локального голосового и экранного агента, а затем показывает три взаимозаменяемых маршрута LLM: прямой Ollama, Hermes API Server или минимальный local-agent-gateway."
                }
              ]
            }
          ]
        },
        {
          "id": "1-chto-imenno-sobiraetsya",
          "title": "1. Что именно собирается",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Итоговая система состоит из независимых компонентов. Их нельзя называть одним словом «агент»: у каждого своя роль.</strong>"
            },
            {
              "type": "table",
              "head": [
                "<strong>Компонент</strong>",
                "<strong>Роль</strong>",
                "<strong>Где работает</strong>",
                "<strong>Обязателен</strong>"
              ],
              "rows": [
                [
                  "Codex",
                  "Инженер: читает репозиторий, планирует, меняет файлы, запускает проверки",
                  "Desktop/CLI",
                  "Нет, если используется Hermes как инженер"
                ],
                [
                  "Hermes Agent",
                  "Инженер и/или OpenAI-совместимый runtime с инструментами",
                  "CLI/Desktop/API server",
                  "Только для Hermes-маршрута"
                ],
                [
                  "Open-LLM-VTuber",
                  "Интерфейс, Live2D, диалог, WebSocket, ASR/TTS orchestration",
                  "Python-сервер + браузер/Electron",
                  "Да"
                ],
                [
                  "Ollama",
                  "Локально запускает LLM/VLM",
                  "127.0.0.1:11434",
                  "Да для локального маршрута"
                ],
                [
                  "Sherpa-ONNX",
                  "Локально распознаёт микрофон",
                  "В процессе VTuber",
                  "Для voice baseline"
                ],
                [
                  "pyttsx3",
                  "Локально озвучивает ответ системным голосом",
                  "В процессе VTuber",
                  "Для локального TTS"
                ],
                [
                  "Local gateway",
                  "Bearer auth, allowlists, request ID, безопасные ошибки и streaming",
                  "127.0.0.1:8642",
                  "Один из gateway-маршрутов"
                ],
                [
                  "OpenRouter",
                  "Необязательный облачный провайдер моделей",
                  "Интернет/API",
                  "Нет"
                ]
              ]
            },
            {
              "type": "text",
              "html": "<strong>Схема потоков</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>Микрофон -&gt; Sherpa-ONNX ASR -----------------------------+</code><br><code>Скриншот/экран -&gt; Open-LLM-VTuber -&gt; выбранный LLM endpoint |</code><br><code>                                                         v</code><br><code>                                    ответ -&gt; pyttsx3 -&gt; динамики</code><br><br><code>Маршрут A: Open-LLM-VTuber -&gt; Ollama:11434</code><br><code>Маршрут B: Open-LLM-VTuber -&gt; Hermes:8642 -&gt; выбранная Hermes-модель</code><br><code>Маршрут C: Open-LLM-VTuber -&gt; local-agent-gateway:8642 -&gt; Ollama:11434</code>"
                }
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Главное различие. </strong>Codex не является постоянным локальным HTTP gateway. Он может создать, настроить и проверить gateway, но Open-LLM-VTuber не может отправлять runtime-запросы непосредственно в текущий чат Codex."
                }
              ]
            }
          ]
        },
        {
          "id": "2-kak-vybrat-marshrut",
          "title": "2. Как выбрать маршрут",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Нужен результат</strong>",
                "<strong>Выбор</strong>",
                "<strong>Почему</strong>"
              ],
              "rows": [
                [
                  "Самый простой полностью локальный VTuber",
                  "Прямой Ollama",
                  "Меньше процессов и конфигурации"
                ],
                [
                  "Hermes, память и инструменты внутри runtime",
                  "Hermes API Server",
                  "OpenAI-compatible API и полный toolset Hermes"
                ],
                [
                  "Минимальный контролируемый шлюз к Ollama",
                  "local-agent-gateway",
                  "Узкий код, allowlists, тесты и нейтральные ошибки"
                ],
                [
                  "Быстро менять облачные модели",
                  "Hermes + OpenRouter или прямой OpenRouter",
                  "Один API, но данные уходят во внешний сервис"
                ]
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Рекомендованный учебный маршрут. </strong>Сначала докажите прямой Ollama baseline. Затем добавьте ровно один gateway. Так ошибка всегда локализуется между уже проверенными границами."
                }
              ]
            }
          ]
        },
        {
          "id": "3-chto-chelovek-vse-ravno-delaet-sam",
          "title": "3. Что человек всё равно делает сам",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Подтверждает установщики и системные диалоги Windows/macOS.",
                "Проходит вход в ChatGPT/Codex, GitHub, Hermes/Nous Portal или OpenRouter.",
                "Даёт браузеру доступ к микрофону, камере или демонстрации экрана.",
                "Произносит тестовую фразу и подтверждает, был ли слышен звук.",
                "Решает, публиковать ли ветку и создавать ли draft PR.",
                "Никогда не вставляет секрет в чат: вводит его локально в защищённый файл или системный prompt."
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Почему AI не может сделать буквально всё. </strong>Авторизация, выбор устройства ввода, прослушивание динамиков и выдача GUI-разрешений являются границами человеческого контроля. Хороший агент доходит до такой границы, сообщает одну точную операцию и после неё продолжает без повторного объяснения."
                }
              ]
            }
          ]
        },
        {
          "id": "4-trebovaniya-k-kompyuteru-i-katalogu",
          "title": "4. Требования к компьютеру и каталогу",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Параметр</strong>",
                "<strong>Windows</strong>",
                "<strong>macOS</strong>"
              ],
              "rows": [
                [
                  "ОС",
                  "Windows 10/11 x64; PowerShell 7 желательно",
                  "macOS 13+ желательно; Intel или Apple Silicon"
                ],
                [
                  "Терминал",
                  "Windows Terminal + PowerShell 7",
                  "Terminal/iTerm + zsh"
                ],
                [
                  "Рабочая папка",
                  "C:\\dev\\agent-lab",
                  "~/dev/agent-lab"
                ],
                [
                  "Путь",
                  "ASCII, без OneDrive и длинных имён",
                  "Без iCloud Drive и сложных пробелов"
                ],
                [
                  "RAM",
                  "16 ГБ минимум для 4B VLM; больше лучше",
                  "16 ГБ unified memory минимум; больше лучше"
                ],
                [
                  "Свободное место",
                  "Не менее 15-25 ГБ",
                  "Не менее 15-25 ГБ"
                ]
              ]
            }
          ]
        },
        {
          "id": "5-predvaritelnaya-ustanovka",
          "title": "5. Предварительная установка",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Устанавливайте системные компоненты вручную. AI сначала проверяет наличие и только затем предлагает отсутствующее. Не запускайте одновременно несколько package manager-команд.</strong>"
            }
          ]
        },
        {
          "id": "ustanovka-bazovyh-instrumentov",
          "title": "Установка базовых инструментов",
          "level": 3,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Windows PowerShell 7</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>winget install --id Microsoft.PowerShell --source winget</code><br><code>winget install --id Git.Git -e</code><br><code>winget install --id GitHub.cli -e</code><br><code>winget install --id Gyan.FFmpeg -e</code><br><code>winget install --id astral-sh.uv -e</code><br><code># Ollama: установите официальное Windows-приложение или:</code><br><code>winget install --id Ollama.Ollama -e</code><br><code># После установки закройте и заново откройте Terminal.</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>macOS Terminal (zsh)</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code># Установите Homebrew с brew.sh, если его ещё нет.</code><br><code>brew install git gh ffmpeg uv</code><br><code>brew install --cask ollama</code><br><code># Запустите Ollama из Applications один раз.</code><br><code>exec zsh</code>"
                }
              ]
            }
          ]
        },
        {
          "id": "proverka",
          "title": "Проверка",
          "level": 3,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Windows PowerShell 7</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>pwsh --version</code><br><code>git --version</code><br><code>gh --version</code><br><code>uv --version</code><br><code>ffmpeg -version</code><br><code>ollama --version</code><br><code>gh auth status</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>macOS Terminal (zsh)</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>zsh --version</code><br><code>git --version</code><br><code>gh --version</code><br><code>uv --version</code><br><code>ffmpeg -version</code><br><code>ollama --version</code><br><code>gh auth status</code>"
                }
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Нормальный результат. </strong>Все version-команды завершаются с кодом 0. `gh auth status` может требовать `gh auth login`; токен нельзя копировать в отчёт."
                }
              ]
            }
          ]
        },
        {
          "id": "6-ustanovka-i-zapusk-codex",
          "title": "6. Установка и запуск Codex",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Codex используется здесь как инженерный агент. Выбирайте Desktop-приложение, если важны изображения и удобные разрешения, либо CLI для воспроизводимого терминального процесса.</strong>"
            }
          ]
        },
        {
          "id": "pervyi-zapusk",
          "title": "Первый запуск",
          "level": 3,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Windows PowerShell 7</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code># Рекомендуется официальный Codex Desktop installer.</code><br><code># Для CLI используйте официальный установщик, показанный приложением/документацией.</code><br><code>codex --version</code><br><code>Set-Location C:\\dev\\agent-lab</code><br><code>codex</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>macOS Terminal (zsh)</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code># Установите Codex Desktop для macOS либо официальный CLI.</code><br><code>codex --version</code><br><code>mkdir -p ~/dev/agent-lab</code><br><code>cd ~/dev/agent-lab</code><br><code>codex</code>"
                }
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Windows sandbox. </strong>Если CLI сообщает, что `codex-windows-sandbox-setup.exe` не найден, сначала обновите Codex и перезапустите терминал. Не обходите проблему постоянным полным разрешением. Для разовой read-only команды допустимо выбрать однократное выполнение вне сломанной sandbox только после проверки команды и cwd."
                }
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>macOS permissions. </strong>Первый доступ Codex к каталогу Desktop/Documents, браузеру или автоматизации может вызвать системный диалог Privacy &amp; Security. Разрешайте только нужный каталог или действие; Full Disk Access для практикума не требуется."
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>PROMPT: безопасная проверка Codex</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "Работай как инженер в режиме контролируемого агента на Windows.<br>Текущая рабочая папка: C:\\dev\\agent-lab. Не предполагай, что команды другой ОС работают здесь.<br><br>Перед изменением файлов:<br>1. Покажи карту релевантной части репозитория и входные точки.<br>2. Сформулируй одну атомарную цель.<br>3. Перечисли файлы чтения и изменения.<br>4. Назови риски, предположения и реальные команды проверки.<br>5. Сначала покажи план. Не меняй файлы до моего явного «одобряю».<br><br>Ограничения:<br>- не используй sudo; не выполняй rm -rf, git reset --hard, git clean -fd и force push;<br>- не изменяй main и upstream без отдельного явного разрешения;<br>- не читай и не печатай .env, токены, SSH-ключи, cookies, keychain и полный conf.yaml;<br>- не трогай файлы вне текущего проекта;<br>- не ставь глобальные пакеты без отдельного обоснования;<br>- не скрывай ошибки и не заменяй тест утверждением;<br>- после изменения покажи краткий diff, реальный минимальный тест и git status --short;<br>- если требуется расширение scope, остановись и запроси новый атомарный шаг.<br><br>Классифицируй ошибки как environment, configuration, dependency, source или test limitation.<br>Не задавай вопросов, ответ на которые можно безопасно получить read-only проверкой. Останавливайся<br>только перед секретом, GUI-разрешением ОС, внешней публикацией или расширением scope.<br><br>Текущая задача: только инвентаризировать среду. Выполни Get-Location и версии git, gh, uv, ollama. Ничего не изменяй. Не печатай токены."
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>PROMPT: тот же шаг на macOS</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "Работай как инженер в режиме контролируемого агента на macOS.<br>Текущая рабочая папка: ~/dev/agent-lab. Не предполагай, что команды другой ОС работают здесь.<br><br>Перед изменением файлов:<br>1. Покажи карту релевантной части репозитория и входные точки.<br>2. Сформулируй одну атомарную цель.<br>3. Перечисли файлы чтения и изменения.<br>4. Назови риски, предположения и реальные команды проверки.<br>5. Сначала покажи план. Не меняй файлы до моего явного «одобряю».<br><br>Ограничения:<br>- не используй sudo; не выполняй rm -rf, git reset --hard, git clean -fd и force push;<br>- не изменяй main и upstream без отдельного явного разрешения;<br>- не читай и не печатай .env, токены, SSH-ключи, cookies, keychain и полный conf.yaml;<br>- не трогай файлы вне текущего проекта;<br>- не ставь глобальные пакеты без отдельного обоснования;<br>- не скрывай ошибки и не заменяй тест утверждением;<br>- после изменения покажи краткий diff, реальный минимальный тест и git status --short;<br>- если требуется расширение scope, остановись и запроси новый атомарный шаг.<br><br>Классифицируй ошибки как environment, configuration, dependency, source или test limitation.<br>Не задавай вопросов, ответ на которые можно безопасно получить read-only проверкой. Останавливайся<br>только перед секретом, GUI-разрешением ОС, внешней публикацией или расширением scope.<br><br>Текущая задача: только инвентаризировать среду. Выполни pwd и версии git, gh, uv, ollama. Ничего не изменяй. Не читай Keychain."
                }
              ]
            }
          ]
        },
        {
          "id": "7-ustanovka-i-nastroi-ka-hermes-agent",
          "title": "7. Установка и настройка Hermes Agent",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Hermes может играть две роли: инженерный CLI и постоянный OpenAI-compatible API Server. Не смешивайте их в одном объяснении: интерфейс `hermes chat` не является endpoint, а `hermes gateway` является.</strong>"
            }
          ]
        },
        {
          "id": "ustanovka-hermes",
          "title": "Установка Hermes",
          "level": 3,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Windows PowerShell 7</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>iex (irm https://hermes-agent.nousresearch.com/install.ps1)</code><br><code># Откройте новый PowerShell</code><br><code>hermes --version</code><br><code>hermes doctor</code><br><code>hermes setup --portal</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>macOS Terminal (zsh)</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash</code><br><code>exec zsh</code><br><code>hermes --version</code><br><code>hermes doctor</code><br><code>hermes setup --portal</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>Если Nous Portal не используется, выполните `hermes model` и интерактивно выберите OpenRouter, Ollama или другой провайдер. Ключ вводится только в локальный secret prompt/конфигурацию Hermes.</strong>"
            }
          ]
        },
        {
          "id": "razdelenie-profilei",
          "title": "Разделение профилей",
          "level": 3,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Windows PowerShell 7</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>hermes profile create olv-runtime</code><br><code>olv-runtime setup</code><br><code>olv-runtime tools</code><br><code># Для разработки отдельно:</code><br><code>hermes profile create coder</code><br><code>coder setup</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>macOS Terminal (zsh)</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>hermes profile create olv-runtime</code><br><code>olv-runtime setup</code><br><code>olv-runtime tools</code><br><code># Для разработки отдельно:</code><br><code>hermes profile create coder</code><br><code>coder setup</code>"
                }
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Ограничение toolset. </strong>API Server Hermes даёт доступ к инструментам агента. Для профиля `olv-runtime` отключите terminal, file, browser, code_execution, delegation и cronjob, если VTuber не должен управлять компьютером. Профиль разделяет состояние, но не является filesystem sandbox."
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>Для включения Hermes API Server задайте в конфигурации профиля: enabled=true, host=127.0.0.1, port=8642 и уникальный bearer key. Предпочитайте `hermes dashboard` или `hermes config`; не публикуйте `.env` профиля.</strong>"
            },
            {
              "type": "text",
              "html": "<strong>Схема параметров — не вставляйте реальный ключ в документ или чат</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>API_SERVER_ENABLED=true</code><br><code>API_SERVER_HOST=127.0.0.1</code><br><code>API_SERVER_PORT=8642</code><br><code>API_SERVER_KEY=&lt;UNIQUE_LOCAL_KEY&gt;</code><br><code># CORS не задавайте, если браузер не вызывает Hermes напрямую.</code>"
                }
              ]
            }
          ]
        },
        {
          "id": "zapusk-hermes-api-server",
          "title": "Запуск Hermes API Server",
          "level": 3,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Windows PowerShell 7</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>olv-runtime gateway</code><br><code># В другом окне:</code><br><code>Invoke-RestMethod http://127.0.0.1:8642/health</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>macOS Terminal (zsh)</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>olv-runtime gateway</code><br><code># В другом окне:</code><br><code>curl -fsS http://127.0.0.1:8642/health</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>PROMPT: настроить Hermes как ограниченный runtime</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "Проведи настройку отдельного Hermes-профиля `olv-runtime` как loopback-only OpenAI-compatible API Server.<br>Сначала read-only проверь текущую версию Hermes и актуальный синтаксис через `hermes --help` и `hermes gateway --help`.<br>Не читай и не печатай существующие secret files. Не генерируй ключ в сообщении. Попроси меня один раз ввести новый локальный bearer key через безопасный локальный ввод.<br>Настрой host 127.0.0.1, port 8642, обязательную bearer-аутентификацию и минимальный toolset без terminal, file, browser, code_execution, delegation и cronjob.<br>Не ослабляй CORS. До изменения покажи точные параметры и план. После подтверждения запусти gateway, проверь /health, отрицательный запрос без auth и один обезличенный chat-completions smoke test. В отчёте покажи только статусы, request id и redacted key length."
                }
              ]
            }
          ]
        },
        {
          "id": "8-openrouter-tolko-esli-nuzhen-oblachnyi-marshrut",
          "title": "8. OpenRouter: только если нужен облачный маршрут",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Создайте ключ на сайте OpenRouter и сохраните его только в локальном secret store выбранного клиента.",
                "Проверьте актуальный model id перед каждым экспериментом: бесплатные модели и лимиты меняются.",
                "Не отправляйте закрытый код, `.env`, голосовые записи и несокращённые скриншоты.",
                "В Hermes выберите OpenRouter через `hermes model`; не подставляйте OPENROUTER_API_KEY в командную строку.",
                "В Codex OpenRouter не требуется для работы самого Codex; это отдельный runtime для приложения."
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Граница данных. </strong>Облачная модель получает содержимое prompt и переданные изображения. Локальный Ollama не отправляет inference-запрос в облако, но первоначальная загрузка модели требует сети."
                }
              ]
            }
          ]
        },
        {
          "id": "9-sozdanie-rabochei-papki-i-poluchenie-open-llm-vtuber",
          "title": "9. Создание рабочей папки и получение Open-LLM-VTuber",
          "level": 2,
          "blocks": []
        },
        {
          "id": "klonirovanie",
          "title": "Клонирование",
          "level": 3,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Windows PowerShell 7</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>New-Item -ItemType Directory -Force C:\\dev\\agent-lab | Out-Null</code><br><code>Set-Location C:\\dev\\agent-lab</code><br><code>git clone --recursive https://github.com/Open-LLM-VTuber/Open-LLM-VTuber.git</code><br><code>Set-Location .\\Open-LLM-VTuber</code><br><code>git submodule status</code><br><code>git status --short</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>macOS Terminal (zsh)</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>mkdir -p ~/dev/agent-lab</code><br><code>cd ~/dev/agent-lab</code><br><code>git clone --recursive https://github.com/Open-LLM-VTuber/Open-LLM-VTuber.git</code><br><code>cd Open-LLM-VTuber</code><br><code>git submodule status</code><br><code>git status --short</code>"
                }
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Не используйте Code → Download ZIP. </strong>Frontend является Git submodule. Обычный ZIP может дать `Detail Not Found` и лишить проект корректной Git-истории."
                }
              ]
            }
          ]
        },
        {
          "id": "zavisimosti",
          "title": "Зависимости",
          "level": 3,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Windows PowerShell 7</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>uv sync</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>macOS Terminal (zsh)</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>uv sync</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>Первый запуск может скачать ASR-модели. Не классифицируйте длительное отсутствие вывода как зависание, пока процесс жив и сеть/диск активны.</strong>"
            }
          ]
        },
        {
          "id": "10-vosproizvedenie-upstream-baseline",
          "title": "10. Воспроизведение upstream baseline",
          "level": 2,
          "blocks": []
        },
        {
          "id": "zapusk-bez-izmeneniya-source",
          "title": "Запуск без изменения source",
          "level": 3,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Windows PowerShell 7</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>Set-Location C:\\dev\\agent-lab\\Open-LLM-VTuber</code><br><code>uv sync</code><br><code>uv run run_server.py --verbose</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>macOS Terminal (zsh)</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>cd ~/dev/agent-lab/Open-LLM-VTuber</code><br><code>uv sync</code><br><code>uv run run_server.py --verbose</code>"
                }
              ]
            },
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Успех: сервер слушает `localhost:12393`, браузер получает HTTP 200 и source tree не изменён.",
                "Если `conf.yaml` отсутствует, первый запуск обычно создаёт его; остановите сервер Ctrl+C и проверьте файл по имени, не печатая содержимое.",
                "Если uv cache недоступен только агенту, повторите ту же команду с одноразовым разрешением вне sandbox. Это environment, а не dependency.",
                "Если процесс жив, но порт не слушает, сохраните время ожидания и классифицируйте как environment до появления traceback."
              ]
            }
          ]
        },
        {
          "id": "11-ollama-i-qwen3-vl-4b",
          "title": "11. Ollama и qwen3-vl:4b",
          "level": 2,
          "blocks": []
        },
        {
          "id": "model-i-health-check",
          "title": "Модель и health check",
          "level": 3,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Windows PowerShell 7</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>ollama list</code><br><code>ollama pull qwen3-vl:4b</code><br><code>Invoke-RestMethod http://127.0.0.1:11434/api/tags | Select-Object -ExpandProperty models</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>macOS Terminal (zsh)</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>ollama list</code><br><code>ollama pull qwen3-vl:4b</code><br><code>curl -fsS http://127.0.0.1:11434/api/tags</code>"
                }
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Порт занят. </strong>На Windows Ollama tray app и на macOS Ollama.app уже могут слушать 11434. Не запускайте второй `ollama serve`, если health check работает."
                }
              ]
            }
          ]
        },
        {
          "id": "tekstovyi-smoke-test",
          "title": "Текстовый smoke test",
          "level": 3,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Windows PowerShell 7</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>$body = @{model=&#x27;qwen3-vl:4b&#x27;; messages=@(@{role=&#x27;user&#x27;;content=&#x27;Ответь одним словом: готово&#x27;}); stream=$false} | ConvertTo-Json -Depth 5</code><br><code>Invoke-RestMethod -Method Post -Uri http://127.0.0.1:11434/v1/chat/completions -ContentType &#x27;application/json&#x27; -Body $body</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>macOS Terminal (zsh)</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>curl -sS http://127.0.0.1:11434/v1/chat/completions \\</code><br><code>  -H &#x27;Content-Type: application/json&#x27; \\</code><br><code>  -d &#x27;{&quot;model&quot;:&quot;qwen3-vl:4b&quot;,&quot;messages&quot;:[{&quot;role&quot;:&quot;user&quot;,&quot;content&quot;:&quot;Ответь одним словом: готово&quot;}],&quot;stream&quot;:false}&#x27;</code>"
                }
              ]
            }
          ]
        },
        {
          "id": "12-bezopasnoe-izmenenie-conf-yaml",
          "title": "12. Безопасное изменение conf.yaml",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Версия важнее примера. </strong>Не заменяйте весь `conf.yaml` фрагментом из другого релиза. Сначала найдите существующие ключи и их реальные отступы, затем меняйте только значения."
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>Для прямого Ollama выберите существующий `ollama_llm`. Для gateway выберите существующий `openai_compatible_llm`. В релизе 1.2.1 путь начинается с `character_config`, а не `system_config`.</strong>"
            },
            {
              "type": "table",
              "head": [
                "<strong>Маршрут</strong>",
                "<strong>llm_provider</strong>",
                "<strong>base_url</strong>",
                "<strong>model/key</strong>"
              ],
              "rows": [
                [
                  "Прямой Ollama",
                  "ollama_llm",
                  "http://127.0.0.1:11434",
                  "qwen3-vl:4b; key не нужен"
                ],
                [
                  "Hermes",
                  "openai_compatible_llm",
                  "http://127.0.0.1:8642/v1",
                  "обычно hermes-agent + API_SERVER_KEY"
                ],
                [
                  "local gateway",
                  "openai_compatible_llm",
                  "http://127.0.0.1:8642/v1",
                  "qwen3-vl:4b + GATEWAY_BEARER_TOKEN"
                ]
              ]
            }
          ]
        },
        {
          "id": "backup-i-sintaksicheskaya-proverka",
          "title": "Backup и синтаксическая проверка",
          "level": 3,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Windows PowerShell 7</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>Copy-Item .\\conf.yaml &quot;.\\conf.yaml.before-setup.$(Get-Date -Format yyyyMMdd-HHmmss)&quot;</code><br><code># Откройте файл в локальном редакторе, не выводите его в терминал.</code><br><code>uv run python -c &quot;from pathlib import Path; import yaml; yaml.safe_load(Path(&#x27;conf.yaml&#x27;).read_text(encoding=&#x27;utf-8&#x27;)); print(&#x27;YAML_OK&#x27;)</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>macOS Terminal (zsh)</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>cp conf.yaml &quot;conf.yaml.before-setup.$(date +%Y%m%d-%H%M%S)&quot;</code><br><code># Откройте файл в локальном редакторе, не выводите его в терминал.</code><br><code>uv run python -c &quot;from pathlib import Path; import yaml; yaml.safe_load(Path(&#x27;conf.yaml&#x27;).read_text()); print(&#x27;YAML_OK&#x27;)</code>"
                }
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Реальная ошибка, которую нужно узнавать. </strong>Если `basic_memory_agent` и `llm_provider` имеют одинаковый отступ, provider становится соседним ключом и Pydantic/YAML падает. Исправляйте только отступ конкретной строки, затем полностью разбирайте YAML."
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>PROMPT: точечно настроить conf.yaml</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "Работай только с локальным игнорируемым conf.yaml Open-LLM-VTuber. Не печатай весь файл и секреты.<br>Сначала разберись с реальной YAML-структурой программно и покажи существующие пути для basic_memory_agent.llm_provider, openai_compatible_llm.base_url, llm_api_key и model. Не полагайся на путь из моего текста, если схема отличается.<br>Атомарная цель: настроить выбранный мной маршрут, изменив минимальное число scalar values. Создай timestamped backup. До изменения покажи безопасный redacted diff и дождись подтверждения. После изменения полностью распарсь YAML и проверь точные пути, показывая ключ как &lt;redacted&gt; и только его длину. Source, templates и dependencies не меняй."
                }
              ]
            }
          ]
        },
        {
          "id": "13-lokalnyi-russkii-asr",
          "title": "13. Локальный русский ASR",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Выберите существующий backend `sherpa_onnx_asr`. Модели и допустимые значения определяются текущей версией проекта; не придумывайте имена ключей.</strong>"
            },
            {
              "type": "list",
              "ordered": false,
              "items": [
                "В браузере разрешите микрофон только для localhost:12393.",
                "Сначала используйте наушники либо выключите динамики, чтобы доказать отсутствие feedback loop.",
                "Произнесите короткую фразу с уникальным числом, например «Проверка локального распознавания 4827».",
                "В evidence сохраняйте transcript, но не сохраняйте исходное аудио без необходимости.",
                "Cloud ASR keys и endpoints должны отсутствовать в активном пути."
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Критерий. </strong>Уникальное число и смысл русской фразы распознаны приемлемо; в логах нет cloud request. Ошибки выбора микрофона — environment, неверный backend — configuration."
                }
              ]
            }
          ]
        },
        {
          "id": "14-lokalnyi-tts-cherez-pyttsx3",
          "title": "14. Локальный TTS через pyttsx3",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Список голосов зависит от ОС. На Windows в нашем baseline был `Microsoft Irina Desktop`. На macOS сначала установите русский системный голос в System Settings → Accessibility → Spoken Content/System Voice, затем выберите имя, реально выведенное pyttsx3.</strong>"
            }
          ]
        },
        {
          "id": "spisok-golosov-bez-personalnyh-putei",
          "title": "Список голосов без персональных путей",
          "level": 3,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Windows PowerShell 7</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>uv run python -c &quot;import pyttsx3; e=pyttsx3.init(); [print(i, v.name, getattr(v,&#x27;languages&#x27;,[])) for i,v in enumerate(e.getProperty(&#x27;voices&#x27;),1)]</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>macOS Terminal (zsh)</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>uv run python -c &quot;import pyttsx3; e=pyttsx3.init(); [print(i, v.name, getattr(v,&#x27;languages&#x27;,[])) for i,v in enumerate(e.getProperty(&#x27;voices&#x27;),1)]</code>"
                }
              ]
            }
          ]
        },
        {
          "id": "izolirovannyi-tts-smoke-test",
          "title": "Изолированный TTS smoke test",
          "level": 3,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Windows PowerShell 7</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>uv run python -c &quot;import pyttsx3; e=pyttsx3.init(); v=next(x for x in e.getProperty(&#x27;voices&#x27;) if x.name==&#x27;Microsoft Irina Desktop&#x27;); e.setProperty(&#x27;voice&#x27;,v.id); e.say(&#x27;Проверка локального синтеза 4827&#x27;); e.runAndWait(); print(&#x27;TTS_COMPLETED&#x27;)</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>macOS Terminal (zsh)</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code># Замените &lt;RUSSIAN_VOICE_NAME&gt; только именем из предыдущего вывода.</code><br><code>uv run python -c &quot;import pyttsx3; e=pyttsx3.init(); v=next(x for x in e.getProperty(&#x27;voices&#x27;) if x.name==&#x27;&lt;RUSSIAN_VOICE_NAME&gt;&#x27;); e.setProperty(&#x27;voice&#x27;,v.id); e.say(&#x27;Проверка локального синтеза 4827&#x27;); e.runAndWait(); print(&#x27;TTS_COMPLETED&#x27;)</code>"
                }
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Неинтерактивная сессия. </strong>COMError на Windows или отсутствие слышимого звука в sandbox/SSH не доказывает поломку pyttsx3. Повторите тест в обычном пользовательском Terminal с доступом к аудиосессии. На macOS проверьте выбранный output device и системный голос."
                }
              ]
            }
          ]
        },
        {
          "id": "15-minimalnyi-local-agent-gateway",
          "title": "15. Минимальный local-agent-gateway",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Этот маршрут воспроизводит реализованный нами gateway: loopback-only FastAPI, bearer auth, backend/model allowlists, request ID, ограничение размера, нейтральные 4xx/502, safe logging и streaming passthrough.</strong>"
            }
          ]
        },
        {
          "id": "poluchenie-i-testy",
          "title": "Получение и тесты",
          "level": 3,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Windows PowerShell 7</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>Set-Location C:\\dev\\agent-lab</code><br><code>git clone --branch agent/local-gateway https://github.com/aka-gst/local-agent-gateway.git</code><br><code>Set-Location .\\local-agent-gateway</code><br><code>uv sync --extra test</code><br><code>Copy-Item .\\.env.example .\\.env</code><br><code># Заполните .env локально, не печатая его.</code><br><code>uv run pytest -q</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>macOS Terminal (zsh)</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>cd ~/dev/agent-lab</code><br><code>git clone --branch agent/local-gateway https://github.com/aka-gst/local-agent-gateway.git</code><br><code>cd local-agent-gateway</code><br><code>uv sync --extra test</code><br><code>cp .env.example .env</code><br><code># Заполните .env локально, не печатая его.</code><br><code>uv run pytest -q</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>Имена параметров .env — реальный token не выводить</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>GATEWAY_BEARER_TOKEN=&lt;32 RANDOM BYTES, BASE64URL&gt;</code><br><code>GATEWAY_ALLOWED_BACKENDS=ollama</code><br><code>GATEWAY_ALLOWED_MODELS=qwen3-vl:4b</code><br><code>GATEWAY_DEFAULT_BACKEND=ollama</code><br><code>GATEWAY_OLLAMA_BASE_URL=http://127.0.0.1:11434/v1</code><br><code>GATEWAY_UPSTREAM_TIMEOUT_SECONDS=120</code><br><code>GATEWAY_MAX_REQUEST_BYTES=1048576</code>"
                }
              ]
            }
          ]
        },
        {
          "id": "zapusk-gateway",
          "title": "Запуск gateway",
          "level": 3,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Windows PowerShell 7</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>uv run local-agent-gateway</code><br><code># Отдельное окно:</code><br><code>Invoke-RestMethod http://127.0.0.1:8642/health</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>macOS Terminal (zsh)</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>uv run local-agent-gateway</code><br><code># Отдельное окно:</code><br><code>curl -fsS http://127.0.0.1:8642/health</code>"
                }
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Почему timeout 120. </strong>При первом запросе qwen3-vl:4b может загружаться в память дольше 30 секунд. Сначала измерьте прямой Ollama-вызов; только затем увеличивайте timeout. `upstream unavailable` при рабочем Ollama часто является timeout, а не ошибкой модели."
                }
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Streaming. </strong>Open-LLM-VTuber фактически отправляет `stream=true`. Gateway должен передавать поток без накопления всего ответа, не пересылать клиентский Authorization в Ollama и закрывать upstream при disconnect."
                }
              ]
            }
          ]
        },
        {
          "id": "16-poryadok-zapuska-chetyre-h-okon",
          "title": "16. Порядок запуска четырёх окон",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Окно</strong>",
                "<strong>Каталог</strong>",
                "<strong>Процесс</strong>",
                "<strong>Проверка</strong>"
              ],
              "rows": [
                [
                  "1",
                  "любой",
                  "Ollama.app/tray или ollama serve",
                  "11434 /api/tags"
                ],
                [
                  "2",
                  "local-agent-gateway или Hermes profile",
                  "gateway",
                  "8642 /health"
                ],
                [
                  "3",
                  "Open-LLM-VTuber",
                  "uv run run_server.py --verbose",
                  "12393 HTTP 200"
                ],
                [
                  "4",
                  "любой",
                  "health/smoke commands",
                  "статусы и request id"
                ]
              ]
            }
          ]
        },
        {
          "id": "proverka-portov",
          "title": "Проверка портов",
          "level": 3,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Windows PowerShell 7</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>Get-NetTCPConnection -State Listen -LocalPort 11434,8642,12393 -ErrorAction SilentlyContinue | Select-Object LocalAddress,LocalPort,OwningProcess</code>"
                }
              ]
            },
            {
              "type": "text",
              "html": "<strong>macOS Terminal (zsh)</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>lsof -nP -iTCP:11434 -iTCP:8642 -iTCP:12393 -sTCP:LISTEN</code>"
                }
              ]
            }
          ]
        },
        {
          "id": "17-end-to-end-smoke-tests",
          "title": "17. End-to-end smoke tests",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": true,
              "items": [
                "Text-only: в VTuber отправьте «Ответь одним словом: готово». Должен появиться ответ, gateway — HTTP 200/request id.",
                "Voice: включите microphone capture и произнесите фразу с новым уникальным числом. Сохраните только transcript и PASS/FAIL.",
                "TTS: после одного ответа подтвердите слухом локальное воспроизведение. Затем выключите микрофон.",
                "Loop control: на 15 секунд включите микрофон при динамиках и убедитесь, что новых бесконечных циклов нет; при риске используйте наушники.",
                "Vision: используйте штатную кнопку camera/screen share или file input. Если `document.querySelectorAll(&#x27;input[type=file]&#x27;).length` равно 0, не имитируйте upload: отметьте capability NOT RUN."
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Чего не доказывает UI-ответ. </strong>Сам ответ не доказывает маршрут. Нужны request id/HTTP 200 gateway и лог выбранного upstream без prompt/response содержимого."
                }
              ]
            }
          ]
        },
        {
          "id": "18-ostanovka-i-otkat",
          "title": "18. Остановка и откат",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": true,
              "items": [
                "Ctrl+C в окне Open-LLM-VTuber; дождитесь shutdown complete.",
                "Ctrl+C в окне gateway; проверьте освобождение 8642.",
                "Ollama tray/app можно оставить работающим; `ollama serve` остановите Ctrl+C.",
                "Восстанавливайте conf.yaml только из известного timestamped backup при остановленном VTuber.",
                "Не завершайте неизвестный PID. Сначала определите приложение-владельца порта."
              ]
            }
          ]
        },
        {
          "id": "19-diagnostika-po-simptomam",
          "title": "19. Диагностика по симптомам",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Симптом</strong>",
                "<strong>Класс</strong>",
                "<strong>Минимальная проверка</strong>",
                "<strong>Действие</strong>"
              ],
              "rows": [
                [
                  "uv cache Access denied",
                  "environment",
                  "uv cache dir; повтор той же команды вне sandbox",
                  "Однократное разрешение или локальный cache внутри workspace"
                ],
                [
                  "11434 address in use",
                  "environment",
                  "listener + /api/tags",
                  "Не запускать второй Ollama"
                ],
                [
                  "YAML ParserError",
                  "configuration",
                  "полный safe_load + строки вокруг ошибки",
                  "Исправить только отступ/скаляр"
                ],
                [
                  "Pydantic literal error",
                  "configuration",
                  "список допустимых backend из ошибки/кода",
                  "Использовать точный идентификатор"
                ],
                [
                  "Gateway 401",
                  "configuration",
                  "сравнение token match без вывода",
                  "Одинаковый token и restart"
                ],
                [
                  "Gateway 400 model not allowed",
                  "configuration",
                  "ollama list + allowlist",
                  "Точный model tag"
                ],
                [
                  "Gateway 502 unavailable",
                  "environment/config",
                  "прямой Ollama + время",
                  "Запустить Ollama или увеличить timeout обоснованно"
                ],
                [
                  "Chat endpoint error",
                  "configuration",
                  "base_url, health, model, request id",
                  "Исправить первую упавшую границу"
                ],
                [
                  "pyttsx3 COMError",
                  "environment",
                  "интерактивный пользовательский терминал",
                  "Повторить вне sandbox"
                ],
                [
                  "Нет file input",
                  "capability",
                  "DOM count и UI controls",
                  "NOT RUN; использовать camera/screen share"
                ],
                [
                  "Звук не слышен",
                  "environment/config",
                  "системный output + изолированный TTS",
                  "Выбрать голос/output; не менять source"
                ],
                [
                  "Feedback loop",
                  "configuration/environment",
                  "VAD, mic, 15 секунд",
                  "Наушники; настройка VAD, не повышать громкость"
                ]
              ]
            }
          ]
        },
        {
          "id": "20-odin-master-prompt-dlya-ai",
          "title": "20. Один master prompt для AI",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Этот prompt рассчитан на уже установленного Codex или Hermes. Он не отменяет системные диалоги и подтверждения внешних публикаций, но минимизирует уточняющие вопросы.</strong>"
            },
            {
              "type": "text",
              "html": "<strong>MASTER PROMPT — замените только ОС и маршрут</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "Я хочу с нуля настроить локальную связку Open-LLM-VTuber + Ollama qwen3-vl:4b + sherpa_onnx_asr + pyttsx3_tts на &lt;Windows|macOS&gt;.<br>LLM-маршрут: &lt;прямой Ollama|Hermes API Server|local-agent-gateway&gt;.<br><br>Работай как контролируемый инженер. Самостоятельно выполняй все безопасные read-only проверки и не задавай вопросы, ответ на которые виден в документации, --help, репозитории или локальном состоянии. Перед каждой группой изменений покажи атомарную цель, точные файлы, redacted diff, риски и команды теста; жди моего «одобряю». После одобрения заверши группу целиком.<br><br>Не используй sudo, destructive Git, force push, изменение main/upstream, глобальные пакеты без обоснования. Не читай и не печатай .env, conf.yaml целиком, token, SSH, cookies или keychain. Секрет запрашивай только через безопасный локальный ввод и показывай лишь match/length. Не меняй source Open-LLM-VTuber, templates или dependencies, пока не доказан source defect и я отдельно не расширил scope.<br><br>Порядок:<br>1. Проверь ОС, shell, cwd, git, gh, uv, ffmpeg, ollama и выбранного агента. Классифицируй проблемы.<br>2. Получи Open-LLM-VTuber recursive clone в коротком несинхронизируемом пути; прочитай README/CLAUDE/pyproject/quick-start; покажи карту.<br>3. Выполни upstream baseline: uv sync, verbose server, HTTP 200, shutdown, source-tree unchanged.<br>4. Проверь Ollama, скачай qwen3-vl:4b только после подтверждения, выполни text-only smoke.<br>5. Создай backup conf.yaml и настрой минимальные реальные YAML paths выбранного маршрута; не заменяй файл шаблоном. Полностью распарсь YAML.<br>6. Настрой sherpa_onnx_asr. Получи список pyttsx3 voices без путей; выбери доступный русский системный голос, на Windows предпочти Microsoft Irina Desktop, на macOS используй фактически установленный русский голос.<br>7. Если выбран gateway: настрой loopback 127.0.0.1:8642, bearer auth, allowlists, request id, безопасные ошибки и streaming. Для Hermes отключи опасные toolsets runtime-профиля. Для local-agent-gateway запусти pytest.<br>8. Запусти компоненты в правильном порядке и выполни health checks.<br>9. Остановись для моих ручных действий только четыре раза: browser login, secret input, microphone/screen permission и подтверждение слышимого звука.<br>10. Проведи text, voice, TTS, 15-second loop и доступный vision smoke. Не выдавай NOT RUN за PASS.<br>11. Покажи sanitised evidence: версии, backend names, request id/status, transcript тестовой фразы, TTS user-confirmed, loop result, source-tree status. Не показывай полный prompt/response.<br>12. Останови сервисы в обратном порядке и предложи ровно один следующий шаг.<br><br>Если команда одной ОС не подходит, сначала переведи её в native PowerShell 7 или zsh. Bash heredoc `&lt;&lt;&#x27;PY&#x27;` не используй в PowerShell. При любой ошибке покажи реальный вывод и не продолжай к следующей границе, пока текущая не доказана."
                }
              ]
            }
          ]
        },
        {
          "id": "21-format-itogovogo-otche-ta",
          "title": "21. Формат итогового отчёта",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Шаблон evidence</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<code>ОС: Windows / macOS</code><br><code>Маршрут: direct Ollama / Hermes / local-agent-gateway</code><br><code>Ollama model: &lt;exact tag&gt;</code><br><code>ASR: sherpa_onnx_asr — PASS / FAIL / NOT PROVEN</code><br><code>TTS: pyttsx3_tts, voice=&lt;name&gt; — PASS / FAIL / NOT PROVEN</code><br><code>Gateway: HTTP status=&lt;code&gt;, request_id=&lt;sanitized&gt;</code><br><code>Vision: PASS / FAIL / NOT RUN; method=&lt;screen/camera/file&gt;</code><br><code>Loop control: PASS / FAIL / NOT RUN; observation=15 s</code><br><code>Source tree: unchanged / known diff</code><br><code>Secrets printed: no</code><br><code>Не доказано: &lt;ограничения&gt;</code><br><code>Следующий атомарный шаг: &lt;ровно один&gt;</code>"
                }
              ]
            }
          ]
        },
        {
          "id": "22-oficialnye-istochniki-i-proverka-aktualnosti",
          "title": "22. Официальные источники и проверка актуальности",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Codex: https://developers.openai.com/codex/",
                "Codex Windows sandbox: https://developers.openai.com/codex/windows/",
                "Hermes Agent — установка, Portal и CLI: https://hermes-agent.nousresearch.com/docs/",
                "Hermes API Server: https://hermes-agent.nousresearch.com/docs/user-guide/features/api-server/",
                "Ollama — загрузка для Windows и macOS: https://ollama.com/download",
                "Ollama API: https://docs.ollama.com/api",
                "Open-LLM-VTuber Quick Start: https://docs.llmvtuber.com/en/docs/quick-start/",
                "Open-LLM-VTuber LLM configuration: https://docs.llmvtuber.com/en/docs/user-guide/backend/llm/",
                "OpenRouter API: https://openrouter.ai/docs/api-reference/overview",
                "uv: https://docs.astral.sh/uv/",
                "GitHub CLI: https://cli.github.com/manual/"
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Правило версий. </strong>Команды этого руководства сверены с локальным Open-LLM-VTuber 1.2.1 и окружением практикума. Если точный YAML path или CLI-флаг отличается, сначала прочитайте локальные README/CLAUDE/pyproject и --help, затем меняйте только фактически существующий ключ."
                }
              ]
            }
          ]
        }
      ],
      "summary": "Назначение. Эта часть не требует предыдущего практикума. Она ведёт от чистой Windows- или macOS-системы до локального голосового и экранного агента, а затем показывает три взаимозаменяемых маршрута LLM: прямой Ollama, Hermes API Server или минимальный local-agent-gateway.",
      "words": 2865,
      "estimate_minutes": 20
    },
    {
      "id": "method",
      "title": "Метод и правила работы",
      "kind": "guide",
      "order": 2,
      "sections": [
        {
          "id": "section-1",
          "title": "",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "<strong>Ниже сохранён подробный Codex-практикум V2: 15 экспериментов, отдельные prompts, команды, ошибки и критерии. Используйте его после Части I. Hermes можно подставить как инженерного агента, сохраняя тот же controlled-agent contract; в экспериментах 9-12 отдельно выбирайте Hermes API Server или минимальный gateway, а не оба одновременно.</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>macOS. </strong>Исходные лабораторные команды преимущественно даны для PowerShell. Перед выполнением на macOS используйте таблицу эквивалентов ниже и native zsh-команды из Части I. Не вставляйте PowerShell variables `$env:` и here-strings `@&#x27;...&#x27;@` в zsh."
                }
              ]
            },
            {
              "type": "table",
              "head": [
                "<strong>Задача</strong>",
                "<strong>Windows PowerShell</strong>",
                "<strong>macOS zsh</strong>"
              ],
              "rows": [
                [
                  "Перейти в каталог",
                  "Set-Location PATH",
                  "cd PATH"
                ],
                [
                  "Создать каталог",
                  "New-Item -ItemType Directory",
                  "mkdir -p"
                ],
                [
                  "Скопировать файл",
                  "Copy-Item",
                  "cp"
                ],
                [
                  "Проверить файл",
                  "Test-Path",
                  "test -f"
                ],
                [
                  "Найти listener",
                  "Get-NetTCPConnection",
                  "lsof -nP -iTCP:PORT -sTCP:LISTEN"
                ],
                [
                  "HTTP GET",
                  "Invoke-RestMethod",
                  "curl -fsS"
                ],
                [
                  "Environment",
                  "$env:NAME=&#x27;value&#x27;",
                  "export NAME=&#x27;value&#x27;"
                ],
                [
                  "Активировать venv",
                  ".\\.venv\\Scripts\\Activate.ps1",
                  "source .venv/bin/activate"
                ],
                [
                  "Остановить foreground",
                  "Ctrl+C",
                  "Ctrl+C"
                ],
                [
                  "Многострочный Python",
                  "@&#x27; ... &#x27;@ | uv run python -",
                  "uv run python - &lt;&lt;&#x27;PY&#x27; ... PY"
                ]
              ]
            }
          ]
        },
        {
          "id": "kak-polzovatsya-etoi-versiei",
          "title": "Как пользоваться этой версией",
          "level": 1,
          "blocks": [
            {
              "type": "text",
              "html": "Эта редакция намеренно подробнее сокращённого практикума. Она рассчитана на последовательное выполнение: теория объясняет смысл шага, готовый prompt задаёт Codex безопасный scope, PowerShell-блок даёт точные команды, а раздел проверки помогает не принять красивый ответ модели за доказательство."
            }
          ]
        },
        {
          "id": "glavnoe-pravilo",
          "title": "Главное правило",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Не импровизируйте со scope. </strong>Если Codex обнаружил проблему, которой нет в текущей атомарной цели, зафиксируйте её и остановитесь. Не разрешайте агенту одновременно диагностировать, менять конфигурацию, патчить source и публиковать изменения."
                }
              ]
            }
          ]
        },
        {
          "id": "oboznacheniya",
          "title": "Обозначения",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Обычный PowerShell</strong>",
                "Команда выполняется пользователем вне сессии Codex. Используется для интерактивного входа, UAC, foreground-сервисов и ручных проверок."
              ],
              "rows": [
                [
                  "<strong>Codex CLI</strong>",
                  "Готовый prompt вставляется в новую или текущую сессию Codex из указанной рабочей папки."
                ],
                [
                  "<strong>После подтверждения</strong>",
                  "Не выполняйте следующие команды до того, как Codex покажет план и вы явно ответите «одобряю»."
                ],
                [
                  "<strong>PASS</strong>",
                  "Есть реальный вывод, файл, test result или наблюдение, прямо доказывающее критерий."
                ],
                [
                  "<strong>NOT PROVEN</strong>",
                  "Утверждение возможно верно, но доказательства нет. Это не PASS."
                ]
              ]
            }
          ]
        },
        {
          "id": "mozhno-li-prodolzhit-posle-eksperimenta-8",
          "title": "Можно ли продолжить после эксперимента 8",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Да. Не начинайте заново автоматически. Выполните эксперимент 8.5, сопоставьте старые результаты с критериями и повторите только проверки без доказательств. Если раньше ради pyttsx3 менялся source, функциональный результат можно сохранить, но критерий source tree unchanged остаётся невыполненным до отдельного решения."
            }
          ]
        },
        {
          "id": "roli-komponentov",
          "title": "Роли компонентов",
          "level": 1,
          "blocks": [
            {
              "type": "text",
              "html": "В оригинальной архитектуре Hermes совмещал инженерную роль и runtime gateway. В этой редакции обязанности разделены. Такое разделение важно: Codex не становится HTTP-сервисом только потому, что умеет писать и запускать код."
            },
            {
              "type": "table",
              "head": [
                "<strong>Codex</strong>",
                "Анализирует репозиторий, предлагает план, меняет согласованные файлы, запускает тесты и работает с Git. Не является постоянным endpoint для VTuber."
              ],
              "rows": [
                [
                  "<strong>Local gateway</strong>",
                  "Постоянно слушает 127.0.0.1:8642, принимает OpenAI-compatible запросы, проверяет Bearer token и маршрутизирует только разрешённые backend/model."
                ],
                [
                  "<strong>Ollama</strong>",
                  "Локально обслуживает qwen3-vl:4b через 127.0.0.1:11434/v1. Это основной offline backend."
                ],
                [
                  "<strong>OpenRouter/OpenAI</strong>",
                  "Опциональные cloud backends. В базовых voice/vision экспериментах выключены и не получают аудио."
                ],
                [
                  "<strong>Open-LLM-VTuber</strong>",
                  "Клиентская/runtime-система с WebSocket, Live2D, LLM, ASR и TTS. Подключается к gateway через local conf.yaml."
                ]
              ]
            }
          ]
        },
        {
          "id": "potok-dannyh",
          "title": "Поток данных",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Codex -> читает/меняет/тестирует -> Git workspace\n\nBrowser microphone -> sherpa_onnx_asr -> text\ntext + screenshot -> Open-LLM-VTuber -> Local Gateway :8642\nLocal Gateway -> Ollama :11434/v1 -> qwen3-vl:4b\nmodel response -> pyttsx3_tts -> Windows speakers"
                }
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Приватность. </strong>ASR и TTS остаются локальными. Gateway не должен логировать полный prompt, response, Authorization и речь пользователя."
                }
              ]
            }
          ]
        },
        {
          "id": "rabochaya-struktura-i-okna",
          "title": "Рабочая структура и окна",
          "level": 1,
          "blocks": [
            {
              "type": "note",
              "html": "<strong>Рекомендуемая структура</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "C:\\dev\\agent-lab\\\n|-- Open-LLM-VTuber\\       # upstream exercise repository\n|-- local-agent-gateway\\   # отдельный gateway project\n|-- evidence\\              # только обезличенные доказательства\n|-- praktikum_codex_gateway_ru_v2.docx\n`-- praktikum_codex_gateway_ru_v2.pdf"
                }
              ]
            }
          ]
        },
        {
          "id": "kakie-okna-budut-ispolzovatsya",
          "title": "Какие окна будут использоваться",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "PowerShell A: ollama serve. Окно остаётся открытым, пока работает локальная модель."
            },
            {
              "type": "text",
              "html": "PowerShell B: gateway на 127.0.0.1:8642. Появляется с эксперимента 9."
            },
            {
              "type": "text",
              "html": "PowerShell C: uv run run_server.py --verbose в Open-LLM-VTuber."
            },
            {
              "type": "text",
              "html": "Codex CLI: отдельная сессия для анализа и согласованных изменений."
            },
            {
              "type": "text",
              "html": "Browser: локальная страница VTuber, screenshot и microphone smoke tests."
            }
          ]
        },
        {
          "id": "kak-ponyat-gde-vy-nahodites",
          "title": "Как понять, где вы находитесь",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "powershell",
                  "text": "Get-Location\ngit branch --show-current\ngit status --short"
                }
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Перед копированием команды. </strong>Сверьте путь в prompt с текущим Get-Location. Большинство ошибочных результатов в первых экспериментах возникало из-за запуска rg из C:\\dev\\agent-lab вместо корня Open-LLM-VTuber."
                }
              ]
            }
          ]
        },
        {
          "id": "powershell-komandy-kotorye-chasto-putayut",
          "title": "PowerShell: команды, которые часто путают",
          "level": 1,
          "blocks": []
        },
        {
          "id": "bash-heredoc-ne-rabotaet-v-powershell",
          "title": "Bash heredoc не работает в PowerShell",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Конструкция python - &lt;&lt;&#x27;PY&#x27; предназначена для Bash. В Windows PowerShell используйте here-string, затем передайте его в stdin Python."
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "@'\nprint(\"Проверка PowerShell here-string\")\n'@ | uv run python -"
                }
              ]
            }
          ]
        },
        {
          "id": "ostanovka-foreground-servera",
          "title": "Остановка foreground-сервера",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Нажмите Ctrl+C в том же окне, где запущен сервер. Закрытие окна допустимо как аварийный вариант, но в доказательстве лучше показать штатную остановку и отсутствие listening port."
            }
          ]
        },
        {
          "id": "prosmotrschik-codex",
          "title": "Просмотрщик Codex",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Если вывод открылся на полном экране и внизу написано q to quit, нажмите q. Это закрывает просмотрщик результата, а не удаляет задачу и не останавливает систему."
            }
          ]
        },
        {
          "id": "krasnaya-stroka-failed-to-paste-image",
          "title": "Красная строка Failed to paste image",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Она означает, что CLI получил команду вставки изображения, но в буфере не было поддерживаемого image format. К sandbox и выполнению текстовой команды это не относится."
            }
          ]
        },
        {
          "id": "nikogda-ne-publikui-te",
          "title": "Никогда не публикуйте",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "реальное значение Token из gh auth status;",
                "полный conf.yaml с ключами;",
                "пути C:\\Users\\&lt;имя&gt;\\...;",
                "исходное аудио или transcript личной речи;",
                "скриншоты с чатами, cookies, email и ключами."
              ]
            }
          ]
        },
        {
          "id": "universalnyi-controlled-agent-kontrakt",
          "title": "Универсальный controlled-agent контракт",
          "level": 1,
          "blocks": [
            {
              "type": "text",
              "html": "Этот блок уже встроен в prompts изменяющих экспериментов. Если вы формулируете дополнительный атомарный шаг самостоятельно, начинайте с него."
            },
            {
              "type": "note",
              "html": "<strong>Скопировать целиком</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг."
                }
              ]
            }
          ]
        },
        {
          "id": "kak-otvechat-na-zapros-razresheniya",
          "title": "Как отвечать на запрос разрешения",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Yes, proceed - разрешение только текущей команды. Это безопасный выбор по умолчанию после проверки команды.",
                "Yes, and don&#x27;t ask again - постоянное правило для префикса. Выбирайте только для узкого и понятного read-only префикса.",
                "No / Esc - команда отменяется. Используйте, если scope шире задания, команда читает секреты или меняет неизвестные файлы."
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Важно. </strong>Разрешение выйти за sandbox не делает опасную команду безопасной. Сначала проверяют точный target и причину escalation."
                }
              ]
            }
          ]
        },
        {
          "id": "standart-dokazatelstv",
          "title": "Стандарт доказательств",
          "level": 1,
          "blocks": [
            {
              "type": "text",
              "html": "Практикум считается завершённым не тогда, когда агент написал «готово», а когда каждый критерий связан с фактом. Доказательство должно быть минимальным: достаточно нужных строк лога, а не полного дампа с персональными данными."
            },
            {
              "type": "table",
              "head": [
                "<strong>Команда</strong>",
                "Точный текст команды, рабочая папка и exit code."
              ],
              "rows": [
                [
                  "<strong>Результат</strong>",
                  "Минимальные строки, доказывающие ожидаемое состояние."
                ],
                [
                  "<strong>Ручное наблюдение</strong>",
                  "Допустимо для слышимого TTS и отсутствия feedback loop; явно помечается как ручное."
                ],
                [
                  "<strong>Git</strong>",
                  "git diff для изменяющего шага и git status --short после него."
                ],
                [
                  "<strong>Ограничение</strong>",
                  "Что тест не проверил. Например, health 200 не доказывает генерацию модели."
                ]
              ]
            }
          ]
        },
        {
          "id": "shablon-zapisi-rezultata",
          "title": "Шаблон записи результата",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Эксперимент: <номер и название>\nДата/версия: <дата, commit или версия>\nКоманда: <точная команда>\nExit code: <0 или фактический код>\nДоказательство: <минимальный обезличенный вывод>\nСтатус: PASS / FAIL / NOT PROVEN\nЧто доказано: <одно предложение>\nЧто не доказано: <одно предложение>\nGit status --short: <пусто или фактические строки>\nСледующий шаг: <только один>"
                }
              ]
            }
          ]
        }
      ],
      "summary": "Ниже сохранён подробный Codex-практикум V2: 15 экспериментов, отдельные prompts, команды, ошибки и критерии. Используйте его после Части I. Hermes можно подставить как инженерного агента, сохраняя тот же controlled-agent contract; в экспериментах 9-12 отдельно выбирайте Hermes AP",
      "words": 822,
      "estimate_minutes": 6
    },
    {
      "id": "experiment-01",
      "title": "Эксперимент 1. Инвентаризация среды",
      "kind": "experiment",
      "number": "1",
      "order": 3,
      "sections": [
        {
          "id": "section-1",
          "title": "",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Атомарная цель. </strong>Зафиксировать исходное состояние Windows, инструментов, локальных моделей и Git-репозитория без изменения файлов."
                }
              ]
            }
          ]
        },
        {
          "id": "chto-my-delaem-i-zachem",
          "title": "Что мы делаем и зачем",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Инвентаризация нужна как точка отсчёта. Если дальнейший запуск упадёт, вы сможете отличить исходную проблему окружения от ошибки, появившейся после настройки. Здесь не исправляют найденные проблемы внутри того же шага: сначала сохраняют факты и классифицируют их."
            }
          ]
        },
        {
          "id": "chto-dolzhno-byt-gotovo",
          "title": "Что должно быть готово",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Codex CLI запускается из PowerShell.",
                "Рабочая папка C:\\dev\\agent-lab существует.",
                "Не требуется запускать Open-LLM-VTuber или менять conf.yaml."
              ]
            }
          ]
        },
        {
          "id": "gde-vypolnyat",
          "title": "Где выполнять",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Обычный PowerShell: C:\\dev\\agent-lab. Затем новая сессия Codex из этой же папки."
            }
          ]
        },
        {
          "id": "scope",
          "title": "Scope",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Разрешено</strong>",
                "Только действия, прямо указанные в prompt и командах этого эксперимента."
              ],
              "rows": [
                [
                  "<strong>Запрещено</strong>",
                  "Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок."
                ],
                [
                  "<strong>Результат</strong>",
                  "Артефакты и критерии на третьей странице эксперимента."
                ]
              ]
            },
            {
              "type": "text",
              "html": "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения."
            },
            {
              "type": "note",
              "html": "<strong>PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Проведи read-only инвентаризацию среды C:\\dev\\agent-lab.\n\nНичего не изменяй. Не читай и не выводи содержимое .env, токенов, SSH-ключей, cookies, keychain и других секретов. Не печатай персональные абсолютные пути; заменяй каталог пользователя на %USERPROFILE% или %LOCALAPPDATA%.\n\nПроверь реальные версии Git и uv, состояние GitHub CLI без значения token и scopes, список Ollama-моделей, найденные Git-репозитории и git status --short каждого из них. Если команда завершилась ошибкой, покажи exit code и классифицируй ошибку как environment, configuration, dependency или source.\n\nНичего не исправляй в этом шаге. В конце создай компактную таблицу: проверка, фактический результат, PASS/FAIL, классификация. Подтверди, что файлы не изменялись."
                }
              ]
            }
          ]
        },
        {
          "id": "komandy-powershell",
          "title": "Команды PowerShell",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее."
            },
            {
              "type": "note",
              "html": "<strong>КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "powershell",
                  "text": "Set-Location C:\\dev\\agent-lab\ngit --version\nuv --version\ngh auth status\nollama list\nGet-ChildItem -Directory -Force | Where-Object { Test-Path (Join-Path $_.FullName '.git') }\ngit -C .\\Open-LLM-VTuber status --short"
                }
              ]
            }
          ]
        },
        {
          "id": "kak-dei-stvovat-pri-zaprose-razresheniya",
          "title": "Как действовать при запросе разрешения",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента."
            },
            {
              "type": "text",
              "html": "Для одной безопасной команды выберите разовое Yes, proceed."
            },
            {
              "type": "text",
              "html": "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
            }
          ]
        },
        {
          "id": "chto-dolzhno-poluchitsya",
          "title": "Что должно получиться",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Git и uv печатают версии и exit code 0.",
                "gh auth status сообщает успешный вход, но в отчёт не копируется строка Token.",
                "ollama list показывает qwen3-vl:4b или честно фиксирует отсутствие модели.",
                "git status --short не печатает строк для чистого рабочего дерева."
              ]
            }
          ]
        },
        {
          "id": "tipovye-oshibki-i-dei-stviya",
          "title": "Типовые ошибки и действия",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "orchestrator_helper_launch_failed: обновить Codex CLI и повторить минимальный Get-Location.",
                "Invalid GitHub token: выполнить gh auth login в обычном PowerShell и повторить только проверку.",
                "Ollama не может писать лог из sandbox: запустить ollama serve вне sandbox и разрешить агенту только ollama list."
              ]
            }
          ]
        },
        {
          "id": "artefakty-i-dokazatelstva",
          "title": "Артефакты и доказательства",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Таблица версий без персональных путей.",
                "Список локальных моделей.",
                "Состояние Git-репозитория.",
                "Классификация всех ошибок."
              ]
            }
          ]
        },
        {
          "id": "kriterii-zaversheniya",
          "title": "Критерий завершения",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Каждая команда имеет реальный вывод.",
                "Source tree не изменён.",
                "В отчёте нет token и персональных абсолютных путей."
              ]
            },
            {
              "type": "note",
              "html": "<strong>ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Эксперимент 1: Инвентаризация среды | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Если GitHub и Ollama исправны, перейти к безопасной подготовке fork и рабочей ветки."
                }
              ]
            }
          ]
        }
      ],
      "summary": "Атомарная цель. Зафиксировать исходное состояние Windows, инструментов, локальных моделей и Git-репозитория без изменения файлов.",
      "words": 457,
      "estimate_minutes": 3,
      "task": {
        "objective": [
          "Инвентаризация нужна как точка отсчёта. Если дальнейший запуск упадёт, вы сможете отличить исходную проблему окружения от ошибки, появившейся после настройки. Здесь не исправляют найденные проблемы внутри того же шага: сначала сохраняют факты и классифицируют их."
        ],
        "prerequisites": [
          "Codex CLI запускается из PowerShell.",
          "Рабочая папка C:\\dev\\agent-lab существует.",
          "Не требуется запускать Open-LLM-VTuber или менять conf.yaml."
        ],
        "where": [
          "Обычный PowerShell: C:\\dev\\agent-lab. Затем новая сессия Codex из этой же папки."
        ],
        "scope": [
          "Запрещено — Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок.",
          "Результат — Артефакты и критерии на третьей странице эксперимента.",
          "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения.",
          "PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ"
        ],
        "commands": [
          {
            "language": "powershell",
            "text": "Set-Location C:\\dev\\agent-lab\ngit --version\nuv --version\ngh auth status\nollama list\nGet-ChildItem -Directory -Force | Where-Object { Test-Path (Join-Path $_.FullName '.git') }\ngit -C .\\Open-LLM-VTuber status --short"
          }
        ],
        "command_notes": [
          "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее.",
          "КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ"
        ],
        "permissions": [
          "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента.",
          "Для одной безопасной команды выберите разовое Yes, proceed.",
          "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
        ],
        "expected": [
          "Git и uv печатают версии и exit code 0.",
          "gh auth status сообщает успешный вход, но в отчёт не копируется строка Token.",
          "ollama list показывает qwen3-vl:4b или честно фиксирует отсутствие модели.",
          "git status --short не печатает строк для чистого рабочего дерева."
        ],
        "pitfalls": [
          "orchestrator_helper_launch_failed: обновить Codex CLI и повторить минимальный Get-Location.",
          "Invalid GitHub token: выполнить gh auth login в обычном PowerShell и повторить только проверку.",
          "Ollama не может писать лог из sandbox: запустить ollama serve вне sandbox и разрешить агенту только ollama list."
        ],
        "artifacts": [
          "Таблица версий без персональных путей.",
          "Список локальных моделей.",
          "Состояние Git-репозитория.",
          "Классификация всех ошибок."
        ],
        "done_when": [
          "Каждая команда имеет реальный вывод.",
          "Source tree не изменён.",
          "В отчёте нет token и персональных абсолютных путей.",
          "ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ"
        ],
        "snippets": [
          {
            "section": "Scope",
            "language": "text",
            "text": "Проведи read-only инвентаризацию среды C:\\dev\\agent-lab.\n\nНичего не изменяй. Не читай и не выводи содержимое .env, токенов, SSH-ключей, cookies, keychain и других секретов. Не печатай персональные абсолютные пути; заменяй каталог пользователя на %USERPROFILE% или %LOCALAPPDATA%.\n\nПроверь реальные версии Git и uv, состояние GitHub CLI без значения token и scopes, список Ollama-моделей, найденные Git-репозитории и git status --short каждого из них. Если команда завершилась ошибкой, покажи exit code и классифицируй ошибку как environment, configuration, dependency или source.\n\nНичего не исправляй в этом шаге. В конце создай компактную таблицу: проверка, фактический результат, PASS/FAIL, классификация. Подтверди, что файлы не изменялись."
          },
          {
            "section": "Команды PowerShell",
            "language": "powershell",
            "text": "Set-Location C:\\dev\\agent-lab\ngit --version\nuv --version\ngh auth status\nollama list\nGet-ChildItem -Directory -Force | Where-Object { Test-Path (Join-Path $_.FullName '.git') }\ngit -C .\\Open-LLM-VTuber status --short"
          },
          {
            "section": "Критерий завершения",
            "language": "text",
            "text": "Эксперимент 1: Инвентаризация среды | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Если GitHub и Ollama исправны, перейти к безопасной подготовке fork и рабочей ветки."
          }
        ]
      }
    },
    {
      "id": "experiment-02",
      "title": "Эксперимент 2. Fork, remotes и рабочая ветка",
      "kind": "experiment",
      "number": "2",
      "order": 4,
      "sections": [
        {
          "id": "section-1",
          "title": "",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Атомарная цель. </strong>Убедиться, что работа ведётся в отдельной ветке собственного fork, а upstream main не будет затронут."
                }
              ]
            }
          ]
        },
        {
          "id": "chto-my-delaem-i-zachem",
          "title": "Что мы делаем и зачем",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Ошибки remotes и target branch опаснее ошибки кода: корректный diff, отправленный в чужой upstream или main, нарушает правила практикума. Поэтому сначала проверяют URL, текущую ветку и чистоту дерева, и только затем создают учебную ветку."
            }
          ]
        },
        {
          "id": "chto-dolzhno-byt-gotovo",
          "title": "Что должно быть готово",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Эксперимент 1 завершён.",
                "gh auth status успешен.",
                "Open-LLM-VTuber уже клонирован в C:\\dev\\agent-lab."
              ]
            }
          ]
        },
        {
          "id": "gde-vypolnyat",
          "title": "Где выполнять",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "C:\\dev\\agent-lab\\Open-LLM-VTuber. Сначала read-only аудит в Codex; создание ветки - отдельное подтверждённое изменение."
            }
          ]
        },
        {
          "id": "scope",
          "title": "Scope",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Разрешено</strong>",
                "Только действия, прямо указанные в prompt и командах этого эксперимента."
              ],
              "rows": [
                [
                  "<strong>Запрещено</strong>",
                  "Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок."
                ],
                [
                  "<strong>Результат</strong>",
                  "Артефакты и критерии на третьей странице эксперимента."
                ]
              ]
            },
            {
              "type": "text",
              "html": "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения."
            },
            {
              "type": "note",
              "html": "<strong>PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nТекущая задача: проверить remotes, текущую ветку и статус Open-LLM-VTuber. Определи, какой remote является моим fork, а какой upstream. Не выполняй fetch, pull, push и switch до подтверждения.\n\nЕсли текущая ветка main или master и дерево чистое, предложи создать lab/codex-practicum. Сначала покажи точную команду и попроси подтверждение. После подтверждения создай только ветку, затем покажи git branch --show-current и git status --short."
                }
              ]
            }
          ]
        },
        {
          "id": "komandy-powershell",
          "title": "Команды PowerShell",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее."
            },
            {
              "type": "note",
              "html": "<strong>КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "powershell",
                  "text": "Set-Location C:\\dev\\agent-lab\\Open-LLM-VTuber\ngit remote -v\ngit branch --show-current\ngit status --short\n# Только после подтверждения:\ngit switch -c lab/codex-practicum"
                }
              ]
            }
          ]
        },
        {
          "id": "kak-dei-stvovat-pri-zaprose-razresheniya",
          "title": "Как действовать при запросе разрешения",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента."
            },
            {
              "type": "text",
              "html": "Для одной безопасной команды выберите разовое Yes, proceed."
            },
            {
              "type": "text",
              "html": "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
            }
          ]
        },
        {
          "id": "chto-dolzhno-poluchitsya",
          "title": "Что должно получиться",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "origin указывает на репозиторий пользователя.",
                "upstream, если существует, указывает на исходный Open-LLM-VTuber.",
                "Текущая ветка lab/codex-practicum.",
                "Рабочее дерево остаётся чистым."
              ]
            }
          ]
        },
        {
          "id": "tipovye-oshibki-i-dei-stviya",
          "title": "Типовые ошибки и действия",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Ветка уже существует: не удалять её; выбрать другое имя только после проверки.",
                "origin указывает на upstream: остановиться и исправлять remote отдельным атомарным шагом.",
                "Есть неизвестные изменения: не переключать ветку до их классификации."
              ]
            }
          ]
        },
        {
          "id": "artefakty-i-dokazatelstva",
          "title": "Артефакты и доказательства",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Вывод git remote -v без credential.",
                "Имя рабочей ветки.",
                "git status --short после создания ветки."
              ]
            }
          ]
        },
        {
          "id": "kriterii-zaversheniya",
          "title": "Критерий завершения",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "main/master не изменён.",
                "Нет push и force push.",
                "Изменение ограничено созданием одной локальной ветки."
              ]
            },
            {
              "type": "note",
              "html": "<strong>ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Эксперимент 2: Fork, remotes и рабочая ветка | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Перейти к read-only карте репозитория."
                }
              ]
            }
          ]
        }
      ],
      "summary": "Атомарная цель. Убедиться, что работа ведётся в отдельной ветке собственного fork, а upstream main не будет затронут.",
      "words": 482,
      "estimate_minutes": 3,
      "task": {
        "objective": [
          "Ошибки remotes и target branch опаснее ошибки кода: корректный diff, отправленный в чужой upstream или main, нарушает правила практикума. Поэтому сначала проверяют URL, текущую ветку и чистоту дерева, и только затем создают учебную ветку."
        ],
        "prerequisites": [
          "Эксперимент 1 завершён.",
          "gh auth status успешен.",
          "Open-LLM-VTuber уже клонирован в C:\\dev\\agent-lab."
        ],
        "where": [
          "C:\\dev\\agent-lab\\Open-LLM-VTuber. Сначала read-only аудит в Codex; создание ветки - отдельное подтверждённое изменение."
        ],
        "scope": [
          "Запрещено — Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок.",
          "Результат — Артефакты и критерии на третьей странице эксперимента.",
          "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения.",
          "PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ"
        ],
        "commands": [
          {
            "language": "powershell",
            "text": "Set-Location C:\\dev\\agent-lab\\Open-LLM-VTuber\ngit remote -v\ngit branch --show-current\ngit status --short\n# Только после подтверждения:\ngit switch -c lab/codex-practicum"
          }
        ],
        "command_notes": [
          "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее.",
          "КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ"
        ],
        "permissions": [
          "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента.",
          "Для одной безопасной команды выберите разовое Yes, proceed.",
          "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
        ],
        "expected": [
          "origin указывает на репозиторий пользователя.",
          "upstream, если существует, указывает на исходный Open-LLM-VTuber.",
          "Текущая ветка lab/codex-practicum.",
          "Рабочее дерево остаётся чистым."
        ],
        "pitfalls": [
          "Ветка уже существует: не удалять её; выбрать другое имя только после проверки.",
          "origin указывает на upstream: остановиться и исправлять remote отдельным атомарным шагом.",
          "Есть неизвестные изменения: не переключать ветку до их классификации."
        ],
        "artifacts": [
          "Вывод git remote -v без credential.",
          "Имя рабочей ветки.",
          "git status --short после создания ветки."
        ],
        "done_when": [
          "main/master не изменён.",
          "Нет push и force push.",
          "Изменение ограничено созданием одной локальной ветки.",
          "ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ"
        ],
        "snippets": [
          {
            "section": "Scope",
            "language": "text",
            "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nТекущая задача: проверить remotes, текущую ветку и статус Open-LLM-VTuber. Определи, какой remote является моим fork, а какой upstream. Не выполняй fetch, pull, push и switch до подтверждения.\n\nЕсли текущая ветка main или master и дерево чистое, предложи создать lab/codex-practicum. Сначала покажи точную команду и попроси подтверждение. После подтверждения создай только ветку, затем покажи git branch --show-current и git status --short."
          },
          {
            "section": "Команды PowerShell",
            "language": "powershell",
            "text": "Set-Location C:\\dev\\agent-lab\\Open-LLM-VTuber\ngit remote -v\ngit branch --show-current\ngit status --short\n# Только после подтверждения:\ngit switch -c lab/codex-practicum"
          },
          {
            "section": "Критерий завершения",
            "language": "text",
            "text": "Эксперимент 2: Fork, remotes и рабочая ветка | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Перейти к read-only карте репозитория."
          }
        ]
      }
    },
    {
      "id": "experiment-03",
      "title": "Эксперимент 3. Карта репозитория с Codex",
      "kind": "experiment",
      "number": "3",
      "order": 5,
      "sections": [
        {
          "id": "section-1",
          "title": "",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Атомарная цель. </strong>Получить доказанную карту входных точек, конфигурации, модулей и тестов без записи."
                }
              ]
            }
          ]
        },
        {
          "id": "chto-my-delaem-i-zachem",
          "title": "Что мы делаем и зачем",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Карта репозитория отвечает на практические вопросы: откуда запускается сервер, где валидируется conf.yaml, где создаются LLM/ASR/TTS и какие файлы относятся к upstream baseline. Без этой карты легко исправить симптом в неправильном слое."
            }
          ]
        },
        {
          "id": "chto-dolzhno-byt-gotovo",
          "title": "Что должно быть готово",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Рабочая ветка подготовлена.",
                "Репозиторий открыт именно из его корня.",
                "Старые evidence и отчёты не нужны для анализа."
              ]
            }
          ]
        },
        {
          "id": "gde-vypolnyat",
          "title": "Где выполнять",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Новая сессия Codex из C:\\dev\\agent-lab\\Open-LLM-VTuber."
            }
          ]
        },
        {
          "id": "scope",
          "title": "Scope",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Разрешено</strong>",
                "Только действия, прямо указанные в prompt и командах этого эксперимента."
              ],
              "rows": [
                [
                  "<strong>Запрещено</strong>",
                  "Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок."
                ],
                [
                  "<strong>Результат</strong>",
                  "Артефакты и критерии на третьей странице эксперимента."
                ]
              ]
            },
            {
              "type": "text",
              "html": "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения."
            },
            {
              "type": "note",
              "html": "<strong>PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Проведи read-only архитектурный обзор Open-LLM-VTuber.\n\nНичего не изменяй и не создавай. Не читай .env, evidence, .git, .venv, node_modules, старые логи и backup-файлы. Сначала прочитай README.md, CLAUDE.md или AGENTS.md, pyproject.toml, config_templates/README.md и quick-start документы, если они есть.\n\nПокажи краткое дерево только релевантных каталогов. Затем укажи: серверную точку входа; команду запуска; конфигурационный слой; пользовательский conf.yaml; default templates; фабрики LLM, ASR и TTS; service context; WebSocket routing; frontend; Live2D; тесты и quality commands.\n\nКаждое утверждение подтверди файлом и номером строки. Отдельно перечисли файлы, которые нельзя менять при upstream baseline. В конце выполни git status --short и объясни пустой вывод."
                }
              ]
            }
          ]
        },
        {
          "id": "komandy-powershell",
          "title": "Команды PowerShell",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее."
            },
            {
              "type": "note",
              "html": "<strong>КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "rg --files -g README.md -g CLAUDE.md -g AGENTS.md -g pyproject.toml -g \"*quick*\" -g \"!evidence/**\"\nrg -n \"run_server.py|conf.yaml|tts_config|asr_config|llm_provider\" README.md CLAUDE.md pyproject.toml src config_templates\ngit status --short"
                }
              ]
            }
          ]
        },
        {
          "id": "kak-dei-stvovat-pri-zaprose-razresheniya",
          "title": "Как действовать при запросе разрешения",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента."
            },
            {
              "type": "text",
              "html": "Для одной безопасной команды выберите разовое Yes, proceed."
            },
            {
              "type": "text",
              "html": "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
            }
          ]
        },
        {
          "id": "chto-dolzhno-poluchitsya",
          "title": "Что должно получиться",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Точка входа: run_server.py.",
                "Verbose-команда: uv run run_server.py --verbose.",
                "Схемы: src/open_llm_vtuber/config_manager/.",
                "Frontend отмечен как Git submodule, если это подтверждено локально."
              ]
            }
          ]
        },
        {
          "id": "tipovye-oshibki-i-dei-stviya",
          "title": "Типовые ошибки и действия",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "rg захватил evidence: повторить с -g &#x27;!evidence/**&#x27;.",
                "Номер строки не подтверждает тезис: открыть узкий диапазон и перепроверить.",
                "Агент предлагает менять код: отклонить, так как задача read-only."
              ]
            }
          ]
        },
        {
          "id": "artefakty-i-dokazatelstva",
          "title": "Артефакты и доказательства",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Карта каталогов.",
                "Список входных точек с file:line.",
                "Список запрещённых для baseline файлов."
              ]
            }
          ]
        },
        {
          "id": "kriterii-zaversheniya",
          "title": "Критерий завершения",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "У каждого принятого утверждения есть локальное доказательство.",
                "git status --short пуст.",
                "Ни один файл не создан."
              ]
            },
            {
              "type": "note",
              "html": "<strong>ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Эксперимент 3: Карта репозитория с Codex | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Открыть новую сессию для независимого аудита, не передавая ей первый отчёт."
                }
              ]
            }
          ]
        }
      ],
      "summary": "Атомарная цель. Получить доказанную карту входных точек, конфигурации, модулей и тестов без записи.",
      "words": 407,
      "estimate_minutes": 3,
      "task": {
        "objective": [
          "Карта репозитория отвечает на практические вопросы: откуда запускается сервер, где валидируется conf.yaml, где создаются LLM/ASR/TTS и какие файлы относятся к upstream baseline. Без этой карты легко исправить симптом в неправильном слое."
        ],
        "prerequisites": [
          "Рабочая ветка подготовлена.",
          "Репозиторий открыт именно из его корня.",
          "Старые evidence и отчёты не нужны для анализа."
        ],
        "where": [
          "Новая сессия Codex из C:\\dev\\agent-lab\\Open-LLM-VTuber."
        ],
        "scope": [
          "Запрещено — Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок.",
          "Результат — Артефакты и критерии на третьей странице эксперимента.",
          "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения.",
          "PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ"
        ],
        "commands": [
          {
            "language": "text",
            "text": "rg --files -g README.md -g CLAUDE.md -g AGENTS.md -g pyproject.toml -g \"*quick*\" -g \"!evidence/**\"\nrg -n \"run_server.py|conf.yaml|tts_config|asr_config|llm_provider\" README.md CLAUDE.md pyproject.toml src config_templates\ngit status --short"
          }
        ],
        "command_notes": [
          "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее.",
          "КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ"
        ],
        "permissions": [
          "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента.",
          "Для одной безопасной команды выберите разовое Yes, proceed.",
          "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
        ],
        "expected": [
          "Точка входа: run_server.py.",
          "Verbose-команда: uv run run_server.py --verbose.",
          "Схемы: src/open_llm_vtuber/config_manager/.",
          "Frontend отмечен как Git submodule, если это подтверждено локально."
        ],
        "pitfalls": [
          "rg захватил evidence: повторить с -g &#x27;!evidence/**&#x27;.",
          "Номер строки не подтверждает тезис: открыть узкий диапазон и перепроверить.",
          "Агент предлагает менять код: отклонить, так как задача read-only."
        ],
        "artifacts": [
          "Карта каталогов.",
          "Список входных точек с file:line.",
          "Список запрещённых для baseline файлов."
        ],
        "done_when": [
          "У каждого принятого утверждения есть локальное доказательство.",
          "git status --short пуст.",
          "Ни один файл не создан.",
          "ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ"
        ],
        "snippets": [
          {
            "section": "Scope",
            "language": "text",
            "text": "Проведи read-only архитектурный обзор Open-LLM-VTuber.\n\nНичего не изменяй и не создавай. Не читай .env, evidence, .git, .venv, node_modules, старые логи и backup-файлы. Сначала прочитай README.md, CLAUDE.md или AGENTS.md, pyproject.toml, config_templates/README.md и quick-start документы, если они есть.\n\nПокажи краткое дерево только релевантных каталогов. Затем укажи: серверную точку входа; команду запуска; конфигурационный слой; пользовательский conf.yaml; default templates; фабрики LLM, ASR и TTS; service context; WebSocket routing; frontend; Live2D; тесты и quality commands.\n\nКаждое утверждение подтверди файлом и номером строки. Отдельно перечисли файлы, которые нельзя менять при upstream baseline. В конце выполни git status --short и объясни пустой вывод."
          },
          {
            "section": "Команды PowerShell",
            "language": "text",
            "text": "rg --files -g README.md -g CLAUDE.md -g AGENTS.md -g pyproject.toml -g \"*quick*\" -g \"!evidence/**\"\nrg -n \"run_server.py|conf.yaml|tts_config|asr_config|llm_provider\" README.md CLAUDE.md pyproject.toml src config_templates\ngit status --short"
          },
          {
            "section": "Критерий завершения",
            "language": "text",
            "text": "Эксперимент 3: Карта репозитория с Codex | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Открыть новую сессию для независимого аудита, не передавая ей первый отчёт."
          }
        ]
      }
    },
    {
      "id": "experiment-04",
      "title": "Эксперимент 4. Независимый аудит Codex",
      "kind": "experiment",
      "number": "4",
      "order": 6,
      "sections": [
        {
          "id": "section-1",
          "title": "",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Атомарная цель. </strong>Получить вторую архитектурную карту другой формулировкой и проверить расхождения командами."
                }
              ]
            }
          ]
        },
        {
          "id": "chto-my-delaem-i-zachem",
          "title": "Что мы делаем и зачем",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Второй аудит снижает риск якорения на первом ответе. Важно не просить модель подтвердить уже готовую карту, а сформулировать задачу независимо. Совпадение двух ответов ещё не доказательство; финальным доказательством остаётся локальный файл или команда."
            }
          ]
        },
        {
          "id": "chto-dolzhno-byt-gotovo",
          "title": "Что должно быть готово",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Отчёт эксперимента 3 сохранён вне контекста новой сессии.",
                "Рабочее дерево чистое."
              ]
            }
          ]
        },
        {
          "id": "gde-vypolnyat",
          "title": "Где выполнять",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Новая сессия Codex из корня Open-LLM-VTuber. Не продолжать предыдущий чат."
            }
          ]
        },
        {
          "id": "scope",
          "title": "Scope",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Разрешено</strong>",
                "Только действия, прямо указанные в prompt и командах этого эксперимента."
              ],
              "rows": [
                [
                  "<strong>Запрещено</strong>",
                  "Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок."
                ],
                [
                  "<strong>Результат</strong>",
                  "Артефакты и критерии на третьей странице эксперимента."
                ]
              ]
            },
            {
              "type": "text",
              "html": "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения."
            },
            {
              "type": "note",
              "html": "<strong>PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Проведи независимый read-only аудит Open-LLM-VTuber. Не используй и не ищи отчёты предыдущих экспериментов.\n\nНичего не изменяй. Не читай .env, evidence, .git, .venv, node_modules, логи и backup-файлы. На основании инструкций, документации и кода составь архитектурную карту потока: запуск процесса -> загрузка и валидация конфигурации -> инициализация service context -> LLM/ASR/TTS -> WebSocket -> frontend.\n\nЗатем назови ровно три главных риска воспроизведения upstream baseline на Windows. Для каждого укажи категорию, локальное доказательство file:line, минимальную read-only проверку и безопасный способ устранения. Всё недоказанное пометь NOT PROVEN.\n\nВ конце выполни git status --short. Не сравнивай результат с первым аудитом."
                }
              ]
            }
          ]
        },
        {
          "id": "komandy-powershell",
          "title": "Команды PowerShell",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее."
            },
            {
              "type": "note",
              "html": "<strong>КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "# После получения двух независимых отчётов - в отдельной сессии:\n# передайте тексты как ОТЧЁТ 1 и ОТЧЁТ 2, затем используйте prompt сравнения из документа.\ngit status --short"
                }
              ]
            }
          ]
        },
        {
          "id": "kak-dei-stvovat-pri-zaprose-razresheniya",
          "title": "Как действовать при запросе разрешения",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента."
            },
            {
              "type": "text",
              "html": "Для одной безопасной команды выберите разовое Yes, proceed."
            },
            {
              "type": "text",
              "html": "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
            }
          ]
        },
        {
          "id": "chto-dolzhno-poluchitsya",
          "title": "Что должно получиться",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Получена вторая карта без ссылок на первый отчёт.",
                "Названы ровно три Windows-риска.",
                "После отдельного сравнения создана таблица совпадений и расхождений."
              ]
            }
          ]
        },
        {
          "id": "tipovye-oshibki-i-dei-stviya",
          "title": "Типовые ошибки и действия",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Вторая сессия видит evidence: явно запретить чтение evidence.",
                "Модель выбирает победителя по уверенности: потребовать локальную команду проверки.",
                "Отчёты различаются терминами, но не фактами: нормализовать названия компонентов."
              ]
            }
          ]
        },
        {
          "id": "artefakty-i-dokazatelstva",
          "title": "Артефакты и доказательства",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Два независимых отчёта.",
                "Таблица расхождений.",
                "Реальные команды проверки спорных мест."
              ]
            }
          ]
        },
        {
          "id": "kriterii-zaversheniya",
          "title": "Критерий завершения",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Каждое принятое утверждение доказано локально.",
                "Изменений нет.",
                "Не осталось неотмеченных предположений."
              ]
            },
            {
              "type": "note",
              "html": "<strong>ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Эксперимент 4: Независимый аудит Codex | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Перейти к аудиту допустимых backend-значений текущей версии."
                }
              ]
            }
          ]
        }
      ],
      "summary": "Атомарная цель. Получить вторую архитектурную карту другой формулировкой и проверить расхождения командами.",
      "words": 398,
      "estimate_minutes": 3,
      "task": {
        "objective": [
          "Второй аудит снижает риск якорения на первом ответе. Важно не просить модель подтвердить уже готовую карту, а сформулировать задачу независимо. Совпадение двух ответов ещё не доказательство; финальным доказательством остаётся локальный файл или команда."
        ],
        "prerequisites": [
          "Отчёт эксперимента 3 сохранён вне контекста новой сессии.",
          "Рабочее дерево чистое."
        ],
        "where": [
          "Новая сессия Codex из корня Open-LLM-VTuber. Не продолжать предыдущий чат."
        ],
        "scope": [
          "Запрещено — Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок.",
          "Результат — Артефакты и критерии на третьей странице эксперимента.",
          "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения.",
          "PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ"
        ],
        "commands": [
          {
            "language": "text",
            "text": "# После получения двух независимых отчётов - в отдельной сессии:\n# передайте тексты как ОТЧЁТ 1 и ОТЧЁТ 2, затем используйте prompt сравнения из документа.\ngit status --short"
          }
        ],
        "command_notes": [
          "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее.",
          "КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ"
        ],
        "permissions": [
          "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента.",
          "Для одной безопасной команды выберите разовое Yes, proceed.",
          "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
        ],
        "expected": [
          "Получена вторая карта без ссылок на первый отчёт.",
          "Названы ровно три Windows-риска.",
          "После отдельного сравнения создана таблица совпадений и расхождений."
        ],
        "pitfalls": [
          "Вторая сессия видит evidence: явно запретить чтение evidence.",
          "Модель выбирает победителя по уверенности: потребовать локальную команду проверки.",
          "Отчёты различаются терминами, но не фактами: нормализовать названия компонентов."
        ],
        "artifacts": [
          "Два независимых отчёта.",
          "Таблица расхождений.",
          "Реальные команды проверки спорных мест."
        ],
        "done_when": [
          "Каждое принятое утверждение доказано локально.",
          "Изменений нет.",
          "Не осталось неотмеченных предположений.",
          "ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ"
        ],
        "snippets": [
          {
            "section": "Scope",
            "language": "text",
            "text": "Проведи независимый read-only аудит Open-LLM-VTuber. Не используй и не ищи отчёты предыдущих экспериментов.\n\nНичего не изменяй. Не читай .env, evidence, .git, .venv, node_modules, логи и backup-файлы. На основании инструкций, документации и кода составь архитектурную карту потока: запуск процесса -> загрузка и валидация конфигурации -> инициализация service context -> LLM/ASR/TTS -> WebSocket -> frontend.\n\nЗатем назови ровно три главных риска воспроизведения upstream baseline на Windows. Для каждого укажи категорию, локальное доказательство file:line, минимальную read-only проверку и безопасный способ устранения. Всё недоказанное пометь NOT PROVEN.\n\nВ конце выполни git status --short. Не сравнивай результат с первым аудитом."
          },
          {
            "section": "Команды PowerShell",
            "language": "text",
            "text": "# После получения двух независимых отчётов - в отдельной сессии:\n# передайте тексты как ОТЧЁТ 1 и ОТЧЁТ 2, затем используйте prompt сравнения из документа.\ngit status --short"
          },
          {
            "section": "Критерий завершения",
            "language": "text",
            "text": "Эксперимент 4: Независимый аудит Codex | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Перейти к аудиту допустимых backend-значений текущей версии."
          }
        ]
      }
    },
    {
      "id": "experiment-05",
      "title": "Эксперимент 5. Аудит backend-контрактов",
      "kind": "experiment",
      "number": "5",
      "order": 7,
      "sections": [
        {
          "id": "section-1",
          "title": "",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Атомарная цель. </strong>Зафиксировать точные допустимые значения LLM, ASR и TTS из Pydantic-схемы версии 1.2.1."
                }
              ]
            }
          ]
        },
        {
          "id": "chto-my-delaem-i-zachem",
          "title": "Что мы делаем и зачем",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Названия backend являются контрактом. Строка, похожая на имя Python-класса, может не пройти Literal-валидацию. Поэтому значения нельзя угадывать по названию файла или старой инструкции: их берут из схемы установленного commit."
            }
          ]
        },
        {
          "id": "chto-dolzhno-byt-gotovo",
          "title": "Что должно быть готово",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Архитектурная карта указывает config_manager.",
                "Никакие настройки пока не меняются."
              ]
            }
          ]
        },
        {
          "id": "gde-vypolnyat",
          "title": "Где выполнять",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Read-only сессия Codex в корне репозитория."
            }
          ]
        },
        {
          "id": "scope",
          "title": "Scope",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Разрешено</strong>",
                "Только действия, прямо указанные в prompt и командах этого эксперимента."
              ],
              "rows": [
                [
                  "<strong>Запрещено</strong>",
                  "Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок."
                ],
                [
                  "<strong>Результат</strong>",
                  "Артефакты и критерии на третьей странице эксперимента."
                ]
              ]
            },
            {
              "type": "text",
              "html": "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения."
            },
            {
              "type": "note",
              "html": "<strong>PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Проведи read-only аудит backend-контрактов текущего Open-LLM-VTuber.\n\nНичего не изменяй. Прочитай только Pydantic-конфигурации LLM, ASR и TTS, default conf template и соответствующие factory-файлы. Покажи точные YAML paths и все Literal-значения для asr_model и tts_model. Отдельно подтверди наличие ollama_llm, openai_compatible_llm, sherpa_onnx_asr и pyttsx3_tts.\n\nДля четырёх выбранных backend укажи: точное значение; YAML path; schema file:line; factory file:line; обязательные дочерние ключи. Не раскрывай содержимое пользовательского conf.yaml. В конце git status --short."
                }
              ]
            }
          ]
        },
        {
          "id": "komandy-powershell",
          "title": "Команды PowerShell",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее."
            },
            {
              "type": "note",
              "html": "<strong>КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "rg -n \"asr_model: Literal|tts_model: Literal|ollama_llm|openai_compatible_llm\" src\\open_llm_vtuber\\config_manager src\\open_llm_vtuber\\agent config_templates\\conf.default.yaml\ngit status --short"
                }
              ]
            }
          ]
        },
        {
          "id": "kak-dei-stvovat-pri-zaprose-razresheniya",
          "title": "Как действовать при запросе разрешения",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента."
            },
            {
              "type": "text",
              "html": "Для одной безопасной команды выберите разовое Yes, proceed."
            },
            {
              "type": "text",
              "html": "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
            }
          ]
        },
        {
          "id": "chto-dolzhno-poluchitsya",
          "title": "Что должно получиться",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "ASR содержит sherpa_onnx_asr.",
                "TTS содержит pyttsx3_tts.",
                "LLM pool содержит ollama_llm и openai_compatible_llm.",
                "Пути начинаются с character_config для ASR/TTS и agent_config для LLM."
              ]
            }
          ]
        },
        {
          "id": "tipovye-oshibki-i-dei-stviya",
          "title": "Типовые ошибки и действия",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Literal error: переписать значение ровно как в схеме.",
                "Старый conf.yaml другой версии: не чинить шаблон, а классифицировать migration/configuration.",
                "Комментарий template расходится со схемой: схема является источником истины для валидации."
              ]
            }
          ]
        },
        {
          "id": "artefakty-i-dokazatelstva",
          "title": "Артефакты и доказательства",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Таблица точных значений.",
                "YAML paths.",
                "Ссылки file:line на schema и factory."
              ]
            }
          ]
        },
        {
          "id": "kriterii-zaversheniya",
          "title": "Критерий завершения",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Ни одно значение не угадано.",
                "Пользовательский conf.yaml не опубликован.",
                "Source tree неизменен."
              ]
            },
            {
              "type": "note",
              "html": "<strong>ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Эксперимент 5: Аудит backend-контрактов | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Перейти к чистому upstream baseline до локальной настройки моделей."
                }
              ]
            }
          ]
        }
      ],
      "summary": "Атомарная цель. Зафиксировать точные допустимые значения LLM, ASR и TTS из Pydantic-схемы версии 1.2.1.",
      "words": 353,
      "estimate_minutes": 3,
      "task": {
        "objective": [
          "Названия backend являются контрактом. Строка, похожая на имя Python-класса, может не пройти Literal-валидацию. Поэтому значения нельзя угадывать по названию файла или старой инструкции: их берут из схемы установленного commit."
        ],
        "prerequisites": [
          "Архитектурная карта указывает config_manager.",
          "Никакие настройки пока не меняются."
        ],
        "where": [
          "Read-only сессия Codex в корне репозитория."
        ],
        "scope": [
          "Запрещено — Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок.",
          "Результат — Артефакты и критерии на третьей странице эксперимента.",
          "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения.",
          "PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ"
        ],
        "commands": [
          {
            "language": "text",
            "text": "rg -n \"asr_model: Literal|tts_model: Literal|ollama_llm|openai_compatible_llm\" src\\open_llm_vtuber\\config_manager src\\open_llm_vtuber\\agent config_templates\\conf.default.yaml\ngit status --short"
          }
        ],
        "command_notes": [
          "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее.",
          "КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ"
        ],
        "permissions": [
          "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента.",
          "Для одной безопасной команды выберите разовое Yes, proceed.",
          "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
        ],
        "expected": [
          "ASR содержит sherpa_onnx_asr.",
          "TTS содержит pyttsx3_tts.",
          "LLM pool содержит ollama_llm и openai_compatible_llm.",
          "Пути начинаются с character_config для ASR/TTS и agent_config для LLM."
        ],
        "pitfalls": [
          "Literal error: переписать значение ровно как в схеме.",
          "Старый conf.yaml другой версии: не чинить шаблон, а классифицировать migration/configuration.",
          "Комментарий template расходится со схемой: схема является источником истины для валидации."
        ],
        "artifacts": [
          "Таблица точных значений.",
          "YAML paths.",
          "Ссылки file:line на schema и factory."
        ],
        "done_when": [
          "Ни одно значение не угадано.",
          "Пользовательский conf.yaml не опубликован.",
          "Source tree неизменен.",
          "ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ"
        ],
        "snippets": [
          {
            "section": "Scope",
            "language": "text",
            "text": "Проведи read-only аудит backend-контрактов текущего Open-LLM-VTuber.\n\nНичего не изменяй. Прочитай только Pydantic-конфигурации LLM, ASR и TTS, default conf template и соответствующие factory-файлы. Покажи точные YAML paths и все Literal-значения для asr_model и tts_model. Отдельно подтверди наличие ollama_llm, openai_compatible_llm, sherpa_onnx_asr и pyttsx3_tts.\n\nДля четырёх выбранных backend укажи: точное значение; YAML path; schema file:line; factory file:line; обязательные дочерние ключи. Не раскрывай содержимое пользовательского conf.yaml. В конце git status --short."
          },
          {
            "section": "Команды PowerShell",
            "language": "text",
            "text": "rg -n \"asr_model: Literal|tts_model: Literal|ollama_llm|openai_compatible_llm\" src\\open_llm_vtuber\\config_manager src\\open_llm_vtuber\\agent config_templates\\conf.default.yaml\ngit status --short"
          },
          {
            "section": "Критерий завершения",
            "language": "text",
            "text": "Эксперимент 5: Аудит backend-контрактов | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Перейти к чистому upstream baseline до локальной настройки моделей."
          }
        ]
      }
    },
    {
      "id": "experiment-06",
      "title": "Эксперимент 6. Upstream baseline",
      "kind": "experiment",
      "number": "6",
      "order": 8,
      "sections": [
        {
          "id": "section-1",
          "title": "",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Атомарная цель. </strong>Выполнить uv sync и штатный verbose-запуск без изменения source, templates и dependency versions."
                }
              ]
            }
          ]
        },
        {
          "id": "chto-my-delaem-i-zachem",
          "title": "Что мы делаем и зачем",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Baseline отделяет проблемы проекта от последующей настройки. Если сначала изменить conf.yaml или код, невозможно доказать, что upstream работал в исходном виде. uv sync разрешён как штатная материализация уже закреплённых зависимостей; pyproject.toml и uv.lock редактировать нельзя."
            }
          ]
        },
        {
          "id": "chto-dolzhno-byt-gotovo",
          "title": "Что должно быть готово",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Рабочая ветка чистая.",
                "Python 3.10-3.12 доступен через uv.",
                "Порт 12393 свободен или известен процесс, который его занимает."
              ]
            }
          ]
        },
        {
          "id": "gde-vypolnyat",
          "title": "Где выполнять",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Обычный PowerShell в корне Open-LLM-VTuber. Сервер запускается в отдельном окне, чтобы его можно было остановить Ctrl+C."
            }
          ]
        },
        {
          "id": "scope",
          "title": "Scope",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Разрешено</strong>",
                "Только действия, прямо указанные в prompt и командах этого эксперимента."
              ],
              "rows": [
                [
                  "<strong>Запрещено</strong>",
                  "Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок."
                ],
                [
                  "<strong>Результат</strong>",
                  "Артефакты и критерии на третьей странице эксперимента."
                ]
              ]
            },
            {
              "type": "text",
              "html": "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения."
            },
            {
              "type": "note",
              "html": "<strong>PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nТекущая задача: только воспроизвести upstream baseline. До команд прочитай README.md, CLAUDE.md, pyproject.toml и quick-start docs. Не меняй source files, templates и dependency versions.\n\nСначала покажи план и команды. После подтверждения выполни uv sync, затем uv run run_server.py --verbose. Сохрани реальный вывод, но обезличь пользовательские пути и содержание речи. Классифицируй каждую ошибку как environment, configuration, dependency или source. Не исправляй ошибку в этом же scope. После проверки останови сервер Ctrl+C и покажи git status --short."
                }
              ]
            }
          ]
        },
        {
          "id": "komandy-powershell",
          "title": "Команды PowerShell",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее."
            },
            {
              "type": "note",
              "html": "<strong>КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "powershell",
                  "text": "Set-Location C:\\dev\\agent-lab\\Open-LLM-VTuber\nuv sync\nuv run run_server.py --verbose\n# Остановка: Ctrl+C\ngit status --short"
                }
              ]
            }
          ]
        },
        {
          "id": "kak-dei-stvovat-pri-zaprose-razresheniya",
          "title": "Как действовать при запросе разрешения",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента."
            },
            {
              "type": "text",
              "html": "Для одной безопасной команды выберите разовое Yes, proceed."
            },
            {
              "type": "text",
              "html": "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
            }
          ]
        },
        {
          "id": "chto-dolzhno-poluchitsya",
          "title": "Что должно получиться",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "uv sync завершается exit code 0 либо выдаёт сохранённую dependency/environment ошибку.",
                "Verbose-лог содержит версию Open-LLM-VTuber 1.2.1 и этапы инициализации.",
                "Локальная страница доступна по адресу из лога.",
                "После Ctrl+C порт освобождён."
              ]
            }
          ]
        },
        {
          "id": "tipovye-oshibki-i-dei-stviya",
          "title": "Типовые ошибки и действия",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Pydantic ValidationError: configuration, не dependency.",
                "Frontend missing: проверить состояние submodule, не изменяя source.",
                "Port already in use: environment; определить процесс read-only командой.",
                "Сервер продолжает работать после теста: остановить Ctrl+C и проверить порт."
              ]
            }
          ]
        },
        {
          "id": "artefakty-i-dokazatelstva",
          "title": "Артефакты и доказательства",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Полный обезличенный uv sync log.",
                "Verbose startup log.",
                "Скриншот локальной страницы без персональных данных.",
                "HTTP/health статус, если endpoint подтверждён документацией."
              ]
            }
          ]
        },
        {
          "id": "kriterii-zaversheniya",
          "title": "Критерий завершения",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Baseline запущен или ошибка воспроизводима и классифицирована.",
                "Dependency versions не менялись.",
                "Source/templates неизменны."
              ]
            },
            {
              "type": "note",
              "html": "<strong>ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Эксперимент 6: Upstream baseline | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: После baseline настроить только локальный Ollama VLM в untracked conf.yaml."
                }
              ]
            }
          ]
        }
      ],
      "summary": "Атомарная цель. Выполнить uv sync и штатный verbose-запуск без изменения source, templates и dependency versions.",
      "words": 516,
      "estimate_minutes": 4,
      "task": {
        "objective": [
          "Baseline отделяет проблемы проекта от последующей настройки. Если сначала изменить conf.yaml или код, невозможно доказать, что upstream работал в исходном виде. uv sync разрешён как штатная материализация уже закреплённых зависимостей; pyproject.toml и uv.lock редактировать нельзя."
        ],
        "prerequisites": [
          "Рабочая ветка чистая.",
          "Python 3.10-3.12 доступен через uv.",
          "Порт 12393 свободен или известен процесс, который его занимает."
        ],
        "where": [
          "Обычный PowerShell в корне Open-LLM-VTuber. Сервер запускается в отдельном окне, чтобы его можно было остановить Ctrl+C."
        ],
        "scope": [
          "Запрещено — Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок.",
          "Результат — Артефакты и критерии на третьей странице эксперимента.",
          "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения.",
          "PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ"
        ],
        "commands": [
          {
            "language": "powershell",
            "text": "Set-Location C:\\dev\\agent-lab\\Open-LLM-VTuber\nuv sync\nuv run run_server.py --verbose\n# Остановка: Ctrl+C\ngit status --short"
          }
        ],
        "command_notes": [
          "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее.",
          "КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ"
        ],
        "permissions": [
          "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента.",
          "Для одной безопасной команды выберите разовое Yes, proceed.",
          "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
        ],
        "expected": [
          "uv sync завершается exit code 0 либо выдаёт сохранённую dependency/environment ошибку.",
          "Verbose-лог содержит версию Open-LLM-VTuber 1.2.1 и этапы инициализации.",
          "Локальная страница доступна по адресу из лога.",
          "После Ctrl+C порт освобождён."
        ],
        "pitfalls": [
          "Pydantic ValidationError: configuration, не dependency.",
          "Frontend missing: проверить состояние submodule, не изменяя source.",
          "Port already in use: environment; определить процесс read-only командой.",
          "Сервер продолжает работать после теста: остановить Ctrl+C и проверить порт."
        ],
        "artifacts": [
          "Полный обезличенный uv sync log.",
          "Verbose startup log.",
          "Скриншот локальной страницы без персональных данных.",
          "HTTP/health статус, если endpoint подтверждён документацией."
        ],
        "done_when": [
          "Baseline запущен или ошибка воспроизводима и классифицирована.",
          "Dependency versions не менялись.",
          "Source/templates неизменны.",
          "ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ"
        ],
        "snippets": [
          {
            "section": "Scope",
            "language": "text",
            "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nТекущая задача: только воспроизвести upstream baseline. До команд прочитай README.md, CLAUDE.md, pyproject.toml и quick-start docs. Не меняй source files, templates и dependency versions.\n\nСначала покажи план и команды. После подтверждения выполни uv sync, затем uv run run_server.py --verbose. Сохрани реальный вывод, но обезличь пользовательские пути и содержание речи. Классифицируй каждую ошибку как environment, configuration, dependency или source. Не исправляй ошибку в этом же scope. После проверки останови сервер Ctrl+C и покажи git status --short."
          },
          {
            "section": "Команды PowerShell",
            "language": "powershell",
            "text": "Set-Location C:\\dev\\agent-lab\\Open-LLM-VTuber\nuv sync\nuv run run_server.py --verbose\n# Остановка: Ctrl+C\ngit status --short"
          },
          {
            "section": "Критерий завершения",
            "language": "text",
            "text": "Эксперимент 6: Upstream baseline | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: После baseline настроить только локальный Ollama VLM в untracked conf.yaml."
          }
        ]
      }
    },
    {
      "id": "experiment-07",
      "title": "Эксперимент 7. Локальный Ollama VLM baseline",
      "kind": "experiment",
      "number": "7",
      "order": 9,
      "sections": [
        {
          "id": "section-1",
          "title": "",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Атомарная цель. </strong>Подключить qwen3-vl:4b через существующий ollama_llm, изменив только локальный conf.yaml."
                }
              ]
            }
          ]
        },
        {
          "id": "chto-my-delaem-i-zachem",
          "title": "Что мы делаем и зачем",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Этот эксперимент доказывает локальный text/vision путь без cloud key. Ollama уже предоставляет OpenAI-compatible endpoint на 11434/v1, поэтому отдельный gateway пока не нужен. Изменение ограничивается пользовательским conf.yaml."
            }
          ]
        },
        {
          "id": "chto-dolzhno-byt-gotovo",
          "title": "Что должно быть готово",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "ollama list показывает qwen3-vl:4b.",
                "ollama serve слушает 127.0.0.1:11434.",
                "Upstream baseline завершён."
              ]
            }
          ]
        },
        {
          "id": "gde-vypolnyat",
          "title": "Где выполнять",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Codex в корне репозитория; Ollama запущена в отдельном обычном PowerShell."
            }
          ]
        },
        {
          "id": "scope",
          "title": "Scope",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Разрешено</strong>",
                "Только действия, прямо указанные в prompt и командах этого эксперимента."
              ],
              "rows": [
                [
                  "<strong>Запрещено</strong>",
                  "Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок."
                ],
                [
                  "<strong>Результат</strong>",
                  "Артефакты и критерии на третьей странице эксперимента."
                ]
              ]
            },
            {
              "type": "text",
              "html": "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения."
            },
            {
              "type": "note",
              "html": "<strong>PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nТекущая задача: изменить только локальный conf.yaml, который не tracked Git. Не меняй source code, templates и dependencies.\n\nСначала найди существующие keys character_config.agent_config.agent_settings.basic_memory_agent.llm_provider и character_config.agent_config.llm_configs.ollama_llm. Покажи текущие значения без секретов и точечный YAML plan. После подтверждения установи llm_provider: ollama_llm, base_url: http://127.0.0.1:11434/v1 и model: qwen3-vl:4b. Остальные keys не перестраивай.\n\nПосле изменения покажи diff только выбранных YAML keys без персонального содержимого, запусти сервер verbose и предложи text-only и screenshot smoke tests. В конце git status --short."
                }
              ]
            }
          ]
        },
        {
          "id": "komandy-powershell",
          "title": "Команды PowerShell",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее."
            },
            {
              "type": "note",
              "html": "<strong>КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "bash",
                  "text": "ollama serve\n# Во втором окне:\nollama list\nSet-Location C:\\dev\\agent-lab\\Open-LLM-VTuber\nuv run run_server.py --verbose"
                }
              ]
            }
          ]
        },
        {
          "id": "kak-dei-stvovat-pri-zaprose-razresheniya",
          "title": "Как действовать при запросе разрешения",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента."
            },
            {
              "type": "text",
              "html": "Для одной безопасной команды выберите разовое Yes, proceed."
            },
            {
              "type": "text",
              "html": "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
            }
          ]
        },
        {
          "id": "chto-dolzhno-poluchitsya",
          "title": "Что должно получиться",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Лог инициализации указывает ollama_llm и qwen3-vl:4b.",
                "Text-only запрос получает осмысленный ответ.",
                "Screenshot без личных данных описан моделью.",
                "Нет обращения к cloud API."
              ]
            }
          ]
        },
        {
          "id": "tipovye-oshibki-i-dei-stviya",
          "title": "Типовые ошибки и действия",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Connection refused 11434: запустить ollama serve вне sandbox.",
                "Model not found: проверить точное имя ollama list.",
                "Vision ignored: убедиться, что выбран VLM и payload содержит image по фактическому frontend path.",
                "conf.yaml tracked: остановиться и проверить .gitignore."
              ]
            }
          ]
        },
        {
          "id": "artefakty-i-dokazatelstva",
          "title": "Артефакты и доказательства",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Три изменённых значения без полного conf.yaml.",
                "Text-only ответ.",
                "Screenshot и краткий vision-ответ.",
                "Обезличенный verbose log."
              ]
            }
          ]
        },
        {
          "id": "kriterii-zaversheniya",
          "title": "Критерий завершения",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Оба smoke test прошли.",
                "Cloud key не требовался.",
                "Source/templates/dependencies неизменны."
              ]
            },
            {
              "type": "note",
              "html": "<strong>ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Эксперимент 7: Локальный Ollama VLM baseline | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Настроить локальный русский ASR/TTS baseline."
                }
              ]
            }
          ]
        }
      ],
      "summary": "Атомарная цель. Подключить qwen3-vl:4b через существующий ollama_llm, изменив только локальный conf.yaml.",
      "words": 475,
      "estimate_minutes": 3,
      "task": {
        "objective": [
          "Этот эксперимент доказывает локальный text/vision путь без cloud key. Ollama уже предоставляет OpenAI-compatible endpoint на 11434/v1, поэтому отдельный gateway пока не нужен. Изменение ограничивается пользовательским conf.yaml."
        ],
        "prerequisites": [
          "ollama list показывает qwen3-vl:4b.",
          "ollama serve слушает 127.0.0.1:11434.",
          "Upstream baseline завершён."
        ],
        "where": [
          "Codex в корне репозитория; Ollama запущена в отдельном обычном PowerShell."
        ],
        "scope": [
          "Запрещено — Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок.",
          "Результат — Артефакты и критерии на третьей странице эксперимента.",
          "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения.",
          "PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ"
        ],
        "commands": [
          {
            "language": "bash",
            "text": "ollama serve\n# Во втором окне:\nollama list\nSet-Location C:\\dev\\agent-lab\\Open-LLM-VTuber\nuv run run_server.py --verbose"
          }
        ],
        "command_notes": [
          "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее.",
          "КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ"
        ],
        "permissions": [
          "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента.",
          "Для одной безопасной команды выберите разовое Yes, proceed.",
          "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
        ],
        "expected": [
          "Лог инициализации указывает ollama_llm и qwen3-vl:4b.",
          "Text-only запрос получает осмысленный ответ.",
          "Screenshot без личных данных описан моделью.",
          "Нет обращения к cloud API."
        ],
        "pitfalls": [
          "Connection refused 11434: запустить ollama serve вне sandbox.",
          "Model not found: проверить точное имя ollama list.",
          "Vision ignored: убедиться, что выбран VLM и payload содержит image по фактическому frontend path.",
          "conf.yaml tracked: остановиться и проверить .gitignore."
        ],
        "artifacts": [
          "Три изменённых значения без полного conf.yaml.",
          "Text-only ответ.",
          "Screenshot и краткий vision-ответ.",
          "Обезличенный verbose log."
        ],
        "done_when": [
          "Оба smoke test прошли.",
          "Cloud key не требовался.",
          "Source/templates/dependencies неизменны.",
          "ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ"
        ],
        "snippets": [
          {
            "section": "Scope",
            "language": "text",
            "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nТекущая задача: изменить только локальный conf.yaml, который не tracked Git. Не меняй source code, templates и dependencies.\n\nСначала найди существующие keys character_config.agent_config.agent_settings.basic_memory_agent.llm_provider и character_config.agent_config.llm_configs.ollama_llm. Покажи текущие значения без секретов и точечный YAML plan. После подтверждения установи llm_provider: ollama_llm, base_url: http://127.0.0.1:11434/v1 и model: qwen3-vl:4b. Остальные keys не перестраивай.\n\nПосле изменения покажи diff только выбранных YAML keys без персонального содержимого, запусти сервер verbose и предложи text-only и screenshot smoke tests. В конце git status --short."
          },
          {
            "section": "Команды PowerShell",
            "language": "bash",
            "text": "ollama serve\n# Во втором окне:\nollama list\nSet-Location C:\\dev\\agent-lab\\Open-LLM-VTuber\nuv run run_server.py --verbose"
          },
          {
            "section": "Критерий завершения",
            "language": "text",
            "text": "Эксперимент 7: Локальный Ollama VLM baseline | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Настроить локальный русский ASR/TTS baseline."
          }
        ]
      }
    },
    {
      "id": "experiment-08",
      "title": "Эксперимент 8. Русский локальный voice baseline",
      "kind": "experiment",
      "number": "8",
      "order": 10,
      "sections": [
        {
          "id": "section-1",
          "title": "",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Атомарная цель. </strong>Выбрать sherpa_onnx_asr и pyttsx3_tts, проверить русское распознавание и локальное воспроизведение без cloud API."
                }
              ]
            }
          ]
        },
        {
          "id": "chto-my-delaem-i-zachem",
          "title": "Что мы делаем и зачем",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Голосовой цикл состоит из независимых частей: захват микрофона, VAD, ASR, LLM и TTS. Один успешный текстовый ответ не доказывает микрофон или звук. Поэтому сохраняются отдельные доказательства распознавания фразы с уникальным числом и слышимого ответа."
            }
          ]
        },
        {
          "id": "chto-dolzhno-byt-gotovo",
          "title": "Что должно быть готово",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Эксперимент 7 работает.",
                "Микрофон разрешён браузеру на localhost.",
                "В Windows установлен хотя бы один русский SAPI voice."
              ]
            }
          ]
        },
        {
          "id": "gde-vypolnyat",
          "title": "Где выполнять",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Обычный PowerShell для списка voices и сервера; браузер для одного voice cycle."
            }
          ]
        },
        {
          "id": "scope",
          "title": "Scope",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Разрешено</strong>",
                "Только действия, прямо указанные в prompt и командах этого эксперимента."
              ],
              "rows": [
                [
                  "<strong>Запрещено</strong>",
                  "Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок."
                ],
                [
                  "<strong>Результат</strong>",
                  "Артефакты и критерии на третьей странице эксперимента."
                ]
              ]
            },
            {
              "type": "text",
              "html": "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения."
            },
            {
              "type": "note",
              "html": "<strong>PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nТекущая задача: настроить только local voice baseline в conf.yaml. Не меняй source code, templates и dependencies. Не отправляй аудио в cloud API.\n\nСначала найди точные существующие ASR/TTS keys и Pydantic Literal-значения. Покажи план для character_config.asr_config.asr_model = sherpa_onnx_asr и character_config.tts_config.tts_model = pyttsx3_tts. Найди pyttsx3_tts.voice_name, если он поддерживается текущей схемой. После подтверждения измени только эти keys.\n\nЗатем выполни PowerShell-совместимый список pyttsx3 voices, запусти verbose server и проведи один microphone smoke test с фразой «Проверка локального распознавания 4827». В логах не показывай другое содержание речи. После одного ответа выключи микрофон и проверь отсутствие feedback loop."
                }
              ]
            }
          ]
        },
        {
          "id": "komandy-powershell",
          "title": "Команды PowerShell",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее."
            },
            {
              "type": "note",
              "html": "<strong>КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "@'\nimport pyttsx3\nengine = pyttsx3.init()\nfor voice in engine.getProperty(\"voices\"):\n    print(voice.id, voice.name, getattr(voice, \"languages\", []))\n'@ | uv run python -\n\nuv run run_server.py --verbose"
                }
              ]
            }
          ]
        },
        {
          "id": "kak-dei-stvovat-pri-zaprose-razresheniya",
          "title": "Как действовать при запросе разрешения",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента."
            },
            {
              "type": "text",
              "html": "Для одной безопасной команды выберите разовое Yes, proceed."
            },
            {
              "type": "text",
              "html": "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
            }
          ]
        },
        {
          "id": "chto-dolzhno-poluchitsya",
          "title": "Что должно получиться",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Список voices напечатан без персональных путей.",
                "ASR transcript содержит смысл фразы и уникальное число 4827.",
                "Пользователь слышит локальный TTS-ответ.",
                "После выключения микрофона новых циклов нет."
              ]
            }
          ]
        },
        {
          "id": "tipovye-oshibki-i-dei-stviya",
          "title": "Типовые ошибки и действия",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "pyttsx3 literal_error: использовать pyttsx3_tts, а не имя Python-класса.",
                "TTS object has no attribute: это source/backend contract; не патчить source внутри config-only шага.",
                "Русский voice отсутствует: установить Windows language/voice отдельно, не добавлять cloud TTS.",
                "Feedback loop: снизить громкость, включить VAD/echo control и остановить capture после одного цикла."
              ]
            }
          ]
        },
        {
          "id": "artefakty-i-dokazatelstva",
          "title": "Артефакты и доказательства",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Список voices.",
                "Выбранные ASR/TTS keys.",
                "Transcript только тестовой фразы с 4827.",
                "Ручное подтверждение слышимого ответа.",
                "Наблюдение отсутствия loop."
              ]
            }
          ]
        },
        {
          "id": "kriterii-zaversheniya",
          "title": "Критерий завершения",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "ASR приемлемо распознал русский текст без cloud key.",
                "TTS прозвучал локально.",
                "Нет бесконечного feedback loop.",
                "Source tree неизменен."
              ]
            },
            {
              "type": "note",
              "html": "<strong>ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Эксперимент 8: Русский локальный voice baseline | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Если эксперименты 1-8 уже выполнялись, пройти контрольную точку 8.5 вместо полного повтора."
                }
              ]
            }
          ]
        }
      ],
      "summary": "Атомарная цель. Выбрать sherpa_onnx_asr и pyttsx3_tts, проверить русское распознавание и локальное воспроизведение без cloud API.",
      "words": 556,
      "estimate_minutes": 4,
      "task": {
        "objective": [
          "Голосовой цикл состоит из независимых частей: захват микрофона, VAD, ASR, LLM и TTS. Один успешный текстовый ответ не доказывает микрофон или звук. Поэтому сохраняются отдельные доказательства распознавания фразы с уникальным числом и слышимого ответа."
        ],
        "prerequisites": [
          "Эксперимент 7 работает.",
          "Микрофон разрешён браузеру на localhost.",
          "В Windows установлен хотя бы один русский SAPI voice."
        ],
        "where": [
          "Обычный PowerShell для списка voices и сервера; браузер для одного voice cycle."
        ],
        "scope": [
          "Запрещено — Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок.",
          "Результат — Артефакты и критерии на третьей странице эксперимента.",
          "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения.",
          "PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ"
        ],
        "commands": [
          {
            "language": "text",
            "text": "@'\nimport pyttsx3\nengine = pyttsx3.init()\nfor voice in engine.getProperty(\"voices\"):\n    print(voice.id, voice.name, getattr(voice, \"languages\", []))\n'@ | uv run python -\n\nuv run run_server.py --verbose"
          }
        ],
        "command_notes": [
          "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее.",
          "КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ"
        ],
        "permissions": [
          "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента.",
          "Для одной безопасной команды выберите разовое Yes, proceed.",
          "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
        ],
        "expected": [
          "Список voices напечатан без персональных путей.",
          "ASR transcript содержит смысл фразы и уникальное число 4827.",
          "Пользователь слышит локальный TTS-ответ.",
          "После выключения микрофона новых циклов нет."
        ],
        "pitfalls": [
          "pyttsx3 literal_error: использовать pyttsx3_tts, а не имя Python-класса.",
          "TTS object has no attribute: это source/backend contract; не патчить source внутри config-only шага.",
          "Русский voice отсутствует: установить Windows language/voice отдельно, не добавлять cloud TTS.",
          "Feedback loop: снизить громкость, включить VAD/echo control и остановить capture после одного цикла."
        ],
        "artifacts": [
          "Список voices.",
          "Выбранные ASR/TTS keys.",
          "Transcript только тестовой фразы с 4827.",
          "Ручное подтверждение слышимого ответа.",
          "Наблюдение отсутствия loop."
        ],
        "done_when": [
          "ASR приемлемо распознал русский текст без cloud key.",
          "TTS прозвучал локально.",
          "Нет бесконечного feedback loop.",
          "Source tree неизменен.",
          "ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ"
        ],
        "snippets": [
          {
            "section": "Scope",
            "language": "text",
            "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nТекущая задача: настроить только local voice baseline в conf.yaml. Не меняй source code, templates и dependencies. Не отправляй аудио в cloud API.\n\nСначала найди точные существующие ASR/TTS keys и Pydantic Literal-значения. Покажи план для character_config.asr_config.asr_model = sherpa_onnx_asr и character_config.tts_config.tts_model = pyttsx3_tts. Найди pyttsx3_tts.voice_name, если он поддерживается текущей схемой. После подтверждения измени только эти keys.\n\nЗатем выполни PowerShell-совместимый список pyttsx3 voices, запусти verbose server и проведи один microphone smoke test с фразой «Проверка локального распознавания 4827». В логах не показывай другое содержание речи. После одного ответа выключи микрофон и проверь отсутствие feedback loop."
          },
          {
            "section": "Команды PowerShell",
            "language": "text",
            "text": "@'\nimport pyttsx3\nengine = pyttsx3.init()\nfor voice in engine.getProperty(\"voices\"):\n    print(voice.id, voice.name, getattr(voice, \"languages\", []))\n'@ | uv run python -\n\nuv run run_server.py --verbose"
          },
          {
            "section": "Критерий завершения",
            "language": "text",
            "text": "Эксперимент 8: Русский локальный voice baseline | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Если эксперименты 1-8 уже выполнялись, пройти контрольную точку 8.5 вместо полного повтора."
          }
        ]
      }
    },
    {
      "id": "experiment-08-5",
      "title": "Эксперимент 8.5. Контрольная точка продолжения",
      "kind": "experiment",
      "number": "8.5",
      "order": 11,
      "sections": [
        {
          "id": "section-1",
          "title": "",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Атомарная цель. </strong>Сопоставить уже выполненные эксперименты 1-8 с критериями и повторить только недостающие доказательства."
                }
              ]
            }
          ]
        },
        {
          "id": "chto-my-delaem-i-zachem",
          "title": "Что мы делаем и зачем",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Контрольная точка предотвращает две крайности: слепой повтор всей работы и необоснованное предположение, что старый результат соответствует новой методике. Статус «работает» отделяется от статуса «доказано по критерию»."
            }
          ]
        },
        {
          "id": "chto-dolzhno-byt-gotovo",
          "title": "Что должно быть готово",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Собраны доступные логи, скриншоты и Git-история предыдущей работы.",
                "Никакие старые файлы автоматически не удаляются."
              ]
            }
          ]
        },
        {
          "id": "gde-vypolnyat",
          "title": "Где выполнять",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Read-only Codex в корне Open-LLM-VTuber. Артефакты передаются выборочно и без секретов."
            }
          ]
        },
        {
          "id": "scope",
          "title": "Scope",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Разрешено</strong>",
                "Только действия, прямо указанные в prompt и командах этого эксперимента."
              ],
              "rows": [
                [
                  "<strong>Запрещено</strong>",
                  "Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок."
                ],
                [
                  "<strong>Результат</strong>",
                  "Артефакты и критерии на третьей странице эксперимента."
                ]
              ]
            },
            {
              "type": "text",
              "html": "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения."
            },
            {
              "type": "note",
              "html": "<strong>PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Проведи read-only checkpoint экспериментов 1-8. Не меняй файлы и не исправляй историю.\n\nДля каждого эксперимента создай строку: требование, найденное доказательство, PASS/FAIL/NOT PROVEN, чего не хватает, минимальная команда повтора. Не считай словесное утверждение тестом.\n\nОтдельно проверь три возможных отклонения: менялся ли source ради pyttsx3; были ли evidence committed в репозиторий; был ли PR направлен в upstream вместо собственного fork. Не удаляй и не закрывай ничего без нового scope.\n\nВ конце выведи только список минимальных повторов и git status --short."
                }
              ]
            }
          ]
        },
        {
          "id": "komandy-powershell",
          "title": "Команды PowerShell",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее."
            },
            {
              "type": "note",
              "html": "<strong>КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "bash",
                  "text": "git status --short\ngit log --oneline -10\ngit diff --stat\ngit remote -v\ngit branch --show-current"
                }
              ]
            }
          ]
        },
        {
          "id": "kak-dei-stvovat-pri-zaprose-razresheniya",
          "title": "Как действовать при запросе разрешения",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента."
            },
            {
              "type": "text",
              "html": "Для одной безопасной команды выберите разовое Yes, proceed."
            },
            {
              "type": "text",
              "html": "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
            }
          ]
        },
        {
          "id": "chto-dolzhno-poluchitsya",
          "title": "Что должно получиться",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Матрица содержит восемь экспериментов.",
                "Для каждого NOT PROVEN указана одна минимальная проверка.",
                "Отклонения описаны без автоматического исправления."
              ]
            }
          ]
        },
        {
          "id": "tipovye-oshibki-i-dei-stviya",
          "title": "Типовые ошибки и действия",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Старое evidence содержит персональные данные: редактировать копию, не оригинал.",
                "Source менялся: эксперимент может быть функционально успешен, но критерий source unchanged остаётся FAIL.",
                "PR target неверен: требуется отдельное решение, не silent rewrite history."
              ]
            }
          ]
        },
        {
          "id": "artefakty-i-dokazatelstva",
          "title": "Артефакты и доказательства",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Матрица 1-8.",
                "Список недостающих доказательств.",
                "Список отклонений и их влияние."
              ]
            }
          ]
        },
        {
          "id": "kriterii-zaversheniya",
          "title": "Критерий завершения",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Не повторяется уже доказанное.",
                "Нет скрытых FAIL.",
                "Понятна стартовая точка эксперимента 9."
              ]
            },
            {
              "type": "note",
              "html": "<strong>ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Эксперимент 8.5: Контрольная точка продолжения | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Начать отдельный проект минимального gateway."
                }
              ]
            }
          ]
        }
      ],
      "summary": "Атомарная цель. Сопоставить уже выполненные эксперименты 1-8 с критериями и повторить только недостающие доказательства.",
      "words": 370,
      "estimate_minutes": 3,
      "task": {
        "objective": [
          "Контрольная точка предотвращает две крайности: слепой повтор всей работы и необоснованное предположение, что старый результат соответствует новой методике. Статус «работает» отделяется от статуса «доказано по критерию»."
        ],
        "prerequisites": [
          "Собраны доступные логи, скриншоты и Git-история предыдущей работы.",
          "Никакие старые файлы автоматически не удаляются."
        ],
        "where": [
          "Read-only Codex в корне Open-LLM-VTuber. Артефакты передаются выборочно и без секретов."
        ],
        "scope": [
          "Запрещено — Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок.",
          "Результат — Артефакты и критерии на третьей странице эксперимента.",
          "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения.",
          "PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ"
        ],
        "commands": [
          {
            "language": "bash",
            "text": "git status --short\ngit log --oneline -10\ngit diff --stat\ngit remote -v\ngit branch --show-current"
          }
        ],
        "command_notes": [
          "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее.",
          "КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ"
        ],
        "permissions": [
          "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента.",
          "Для одной безопасной команды выберите разовое Yes, proceed.",
          "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
        ],
        "expected": [
          "Матрица содержит восемь экспериментов.",
          "Для каждого NOT PROVEN указана одна минимальная проверка.",
          "Отклонения описаны без автоматического исправления."
        ],
        "pitfalls": [
          "Старое evidence содержит персональные данные: редактировать копию, не оригинал.",
          "Source менялся: эксперимент может быть функционально успешен, но критерий source unchanged остаётся FAIL.",
          "PR target неверен: требуется отдельное решение, не silent rewrite history."
        ],
        "artifacts": [
          "Матрица 1-8.",
          "Список недостающих доказательств.",
          "Список отклонений и их влияние."
        ],
        "done_when": [
          "Не повторяется уже доказанное.",
          "Нет скрытых FAIL.",
          "Понятна стартовая точка эксперимента 9.",
          "ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ"
        ],
        "snippets": [
          {
            "section": "Scope",
            "language": "text",
            "text": "Проведи read-only checkpoint экспериментов 1-8. Не меняй файлы и не исправляй историю.\n\nДля каждого эксперимента создай строку: требование, найденное доказательство, PASS/FAIL/NOT PROVEN, чего не хватает, минимальная команда повтора. Не считай словесное утверждение тестом.\n\nОтдельно проверь три возможных отклонения: менялся ли source ради pyttsx3; были ли evidence committed в репозиторий; был ли PR направлен в upstream вместо собственного fork. Не удаляй и не закрывай ничего без нового scope.\n\nВ конце выведи только список минимальных повторов и git status --short."
          },
          {
            "section": "Команды PowerShell",
            "language": "bash",
            "text": "git status --short\ngit log --oneline -10\ngit diff --stat\ngit remote -v\ngit branch --show-current"
          },
          {
            "section": "Критерий завершения",
            "language": "text",
            "text": "Эксперимент 8.5: Контрольная точка продолжения | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Начать отдельный проект минимального gateway."
          }
        ]
      }
    },
    {
      "id": "experiment-09",
      "title": "Эксперимент 9. Минимальный runtime gateway",
      "kind": "experiment",
      "number": "9",
      "order": 12,
      "sections": [
        {
          "id": "section-1",
          "title": "",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Атомарная цель. </strong>Создать отдельный loopback-сервис с OpenAI-compatible /v1/chat/completions, Bearer auth и allowlist backend/model."
                }
              ]
            }
          ]
        },
        {
          "id": "chto-my-delaem-i-zachem",
          "title": "Что мы делаем и зачем",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Codex является инженерным агентом, но не постоянным HTTP endpoint. Gateway заполняет именно этот runtime-разрыв: приложение получает стабильный локальный API, а маршрутизация на Ollama или разрешённый cloud backend остаётся в одном контролируемом сервисе."
            }
          ]
        },
        {
          "id": "chto-dolzhno-byt-gotovo",
          "title": "Что должно быть готово",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Эксперимент 8.5 завершён.",
                "Папка C:\\dev\\agent-lab\\local-agent-gateway отсутствует либо её состояние предварительно проверено.",
                "Ollama отвечает локально."
              ]
            }
          ]
        },
        {
          "id": "gde-vypolnyat",
          "title": "Где выполнять",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Новый sibling-проект C:\\dev\\agent-lab\\local-agent-gateway. Open-LLM-VTuber в этом эксперименте не меняется."
            }
          ]
        },
        {
          "id": "scope",
          "title": "Scope",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Разрешено</strong>",
                "Только действия, прямо указанные в prompt и командах этого эксперимента."
              ],
              "rows": [
                [
                  "<strong>Запрещено</strong>",
                  "Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок."
                ],
                [
                  "<strong>Результат</strong>",
                  "Артефакты и критерии на третьей странице эксперимента."
                ]
              ]
            },
            {
              "type": "text",
              "html": "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения."
            },
            {
              "type": "note",
              "html": "<strong>PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nТекущая задача: создать отдельный минимальный Python gateway в C:\\dev\\agent-lab\\local-agent-gateway. Не меняй Open-LLM-VTuber.\n\nТребования: FastAPI; GET /health; POST /v1/chat/completions; bind только 127.0.0.1:8642; Bearer auth для /v1; allowlist backend/model; backend ollama по умолчанию; upstream http://127.0.0.1:11434/v1; нейтральные ошибки; timeout; request_id; не логировать Authorization, полный prompt и полный response. Реальные секреты только в untracked .env, в репозитории только .env.example с placeholders.\n\nСначала покажи карту проекта, файлы, схему request flow, зависимости, риски и тесты. После подтверждения создай проект. После каждого изменения покажи git diff и минимальный pytest. Не добавляй OpenRouter/OpenAI вызов, пока локальный Ollama path не проверен."
                }
              ]
            }
          ]
        },
        {
          "id": "komandy-powershell",
          "title": "Команды PowerShell",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее."
            },
            {
              "type": "note",
              "html": "<strong>КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "powershell",
                  "text": "Set-Location C:\\dev\\agent-lab\nuv init local-agent-gateway\nSet-Location .\\local-agent-gateway\nuv add fastapi \"uvicorn[standard]\" httpx pydantic-settings\nuv add --dev pytest pytest-asyncio\nuv run pytest -q\nuv run uvicorn app.main:app --host 127.0.0.1 --port 8642"
                }
              ]
            }
          ]
        },
        {
          "id": "kak-dei-stvovat-pri-zaprose-razresheniya",
          "title": "Как действовать при запросе разрешения",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента."
            },
            {
              "type": "text",
              "html": "Для одной безопасной команды выберите разовое Yes, proceed."
            },
            {
              "type": "text",
              "html": "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
            }
          ]
        },
        {
          "id": "chto-dolzhno-poluchitsya",
          "title": "Что должно получиться",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "GET /health возвращает 200 без auth и без секретов.",
                "POST /v1/chat/completions без Bearer возвращает 401.",
                "Разрешённая модель qwen3-vl:4b проходит к Ollama.",
                "Запрещённый backend/model отклоняется до сетевого вызова."
              ]
            }
          ]
        },
        {
          "id": "tipovye-oshibki-i-dei-stviya",
          "title": "Типовые ошибки и действия",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Port 8642 busy: определить процесс, не менять порт молча.",
                "401 при правильном клиенте: проверить заголовок без печати token.",
                "502/503: проверить Ollama health и timeout.",
                "Секрет попал в Git: остановиться; не переписывать историю без отдельного решения."
              ]
            }
          ]
        },
        {
          "id": "artefakty-i-dokazatelstva",
          "title": "Артефакты и доказательства",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Карта gateway-проекта.",
                "OpenAPI schema.",
                "pytest auth/health/allowlist.",
                "Лог bind 127.0.0.1:8642 без prompt."
              ]
            }
          ]
        },
        {
          "id": "kriterii-zaversheniya",
          "title": "Критерий завершения",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Все тесты имеют exit code 0.",
                "Gateway не слушает 0.0.0.0.",
                "Секреты не tracked.",
                "Open-LLM-VTuber неизменен."
              ]
            },
            {
              "type": "note",
              "html": "<strong>ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Эксперимент 9: Минимальный runtime gateway | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Подключить Open-LLM-VTuber к gateway только через local conf.yaml."
                }
              ]
            }
          ]
        }
      ],
      "summary": "Атомарная цель. Создать отдельный loopback-сервис с OpenAI-compatible /v1/chat/completions, Bearer auth и allowlist backend/model.",
      "words": 527,
      "estimate_minutes": 4,
      "task": {
        "objective": [
          "Codex является инженерным агентом, но не постоянным HTTP endpoint. Gateway заполняет именно этот runtime-разрыв: приложение получает стабильный локальный API, а маршрутизация на Ollama или разрешённый cloud backend остаётся в одном контролируемом сервисе."
        ],
        "prerequisites": [
          "Эксперимент 8.5 завершён.",
          "Папка C:\\dev\\agent-lab\\local-agent-gateway отсутствует либо её состояние предварительно проверено.",
          "Ollama отвечает локально."
        ],
        "where": [
          "Новый sibling-проект C:\\dev\\agent-lab\\local-agent-gateway. Open-LLM-VTuber в этом эксперименте не меняется."
        ],
        "scope": [
          "Запрещено — Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок.",
          "Результат — Артефакты и критерии на третьей странице эксперимента.",
          "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения.",
          "PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ"
        ],
        "commands": [
          {
            "language": "powershell",
            "text": "Set-Location C:\\dev\\agent-lab\nuv init local-agent-gateway\nSet-Location .\\local-agent-gateway\nuv add fastapi \"uvicorn[standard]\" httpx pydantic-settings\nuv add --dev pytest pytest-asyncio\nuv run pytest -q\nuv run uvicorn app.main:app --host 127.0.0.1 --port 8642"
          }
        ],
        "command_notes": [
          "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее.",
          "КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ"
        ],
        "permissions": [
          "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента.",
          "Для одной безопасной команды выберите разовое Yes, proceed.",
          "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
        ],
        "expected": [
          "GET /health возвращает 200 без auth и без секретов.",
          "POST /v1/chat/completions без Bearer возвращает 401.",
          "Разрешённая модель qwen3-vl:4b проходит к Ollama.",
          "Запрещённый backend/model отклоняется до сетевого вызова."
        ],
        "pitfalls": [
          "Port 8642 busy: определить процесс, не менять порт молча.",
          "401 при правильном клиенте: проверить заголовок без печати token.",
          "502/503: проверить Ollama health и timeout.",
          "Секрет попал в Git: остановиться; не переписывать историю без отдельного решения."
        ],
        "artifacts": [
          "Карта gateway-проекта.",
          "OpenAPI schema.",
          "pytest auth/health/allowlist.",
          "Лог bind 127.0.0.1:8642 без prompt."
        ],
        "done_when": [
          "Все тесты имеют exit code 0.",
          "Gateway не слушает 0.0.0.0.",
          "Секреты не tracked.",
          "Open-LLM-VTuber неизменен.",
          "ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ"
        ],
        "snippets": [
          {
            "section": "Scope",
            "language": "text",
            "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nТекущая задача: создать отдельный минимальный Python gateway в C:\\dev\\agent-lab\\local-agent-gateway. Не меняй Open-LLM-VTuber.\n\nТребования: FastAPI; GET /health; POST /v1/chat/completions; bind только 127.0.0.1:8642; Bearer auth для /v1; allowlist backend/model; backend ollama по умолчанию; upstream http://127.0.0.1:11434/v1; нейтральные ошибки; timeout; request_id; не логировать Authorization, полный prompt и полный response. Реальные секреты только в untracked .env, в репозитории только .env.example с placeholders.\n\nСначала покажи карту проекта, файлы, схему request flow, зависимости, риски и тесты. После подтверждения создай проект. После каждого изменения покажи git diff и минимальный pytest. Не добавляй OpenRouter/OpenAI вызов, пока локальный Ollama path не проверен."
          },
          {
            "section": "Команды PowerShell",
            "language": "powershell",
            "text": "Set-Location C:\\dev\\agent-lab\nuv init local-agent-gateway\nSet-Location .\\local-agent-gateway\nuv add fastapi \"uvicorn[standard]\" httpx pydantic-settings\nuv add --dev pytest pytest-asyncio\nuv run pytest -q\nuv run uvicorn app.main:app --host 127.0.0.1 --port 8642"
          },
          {
            "section": "Критерий завершения",
            "language": "text",
            "text": "Эксперимент 9: Минимальный runtime gateway | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Подключить Open-LLM-VTuber к gateway только через local conf.yaml."
          }
        ]
      }
    },
    {
      "id": "experiment-10",
      "title": "Эксперимент 10. Интеграция gateway с Open-LLM-VTuber",
      "kind": "experiment",
      "number": "10",
      "order": 13,
      "sections": [
        {
          "id": "section-1",
          "title": "",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Атомарная цель. </strong>Перенаправить LLM-трафик на 127.0.0.1:8642 только локальной конфигурацией."
                }
              ]
            }
          ]
        },
        {
          "id": "chto-my-delaem-i-zachem",
          "title": "Что мы делаем и зачем",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Интеграция должна доказать, что приложение воспринимает gateway как обычный OpenAI-compatible provider. Если для этого приходится менять factory или templates, граница совместимости не выдержана и эксперимент останавливается до отдельного adapter-шага."
            }
          ]
        },
        {
          "id": "chto-dolzhno-byt-gotovo",
          "title": "Что должно быть готово",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Gateway health зелёный.",
                "Ollama работает.",
                "Известен локальный gateway token, но он не выводится и не сохраняется в evidence."
              ]
            }
          ]
        },
        {
          "id": "gde-vypolnyat",
          "title": "Где выполнять",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Три процесса в отдельных окнах: Ollama, gateway, Open-LLM-VTuber. Codex меняет только local conf.yaml после подтверждения."
            }
          ]
        },
        {
          "id": "scope",
          "title": "Scope",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Разрешено</strong>",
                "Только действия, прямо указанные в prompt и командах этого эксперимента."
              ],
              "rows": [
                [
                  "<strong>Запрещено</strong>",
                  "Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок."
                ],
                [
                  "<strong>Результат</strong>",
                  "Артефакты и критерии на третьей странице эксперимента."
                ]
              ]
            },
            {
              "type": "text",
              "html": "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения."
            },
            {
              "type": "note",
              "html": "<strong>PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nТекущая задача: изменить только локальный untracked conf.yaml Open-LLM-VTuber. Не меняй source, templates и dependencies.\n\nСначала найди точные keys openai_compatible_llm и basic_memory_agent.llm_provider. Покажи plan для llm_provider: openai_compatible_llm, base_url: http://127.0.0.1:8642/v1, model: qwen3-vl:4b и llm_api_key: <локальный token>. Не показывай реальное значение token ни до, ни после изменения.\n\nПосле подтверждения измени только эти keys. Запусти verbose server и сделай один text-only smoke test. Сопоставь request_id приложения и gateway. Покажи git status --short."
                }
              ]
            }
          ]
        },
        {
          "id": "komandy-powershell",
          "title": "Команды PowerShell",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее."
            },
            {
              "type": "note",
              "html": "<strong>КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "# Окно 1\nollama serve\n\n# Окно 2\nSet-Location C:\\dev\\agent-lab\\local-agent-gateway\nuv run uvicorn app.main:app --host 127.0.0.1 --port 8642\n\n# Окно 3\nSet-Location C:\\dev\\agent-lab\\Open-LLM-VTuber\nuv run run_server.py --verbose"
                }
              ]
            }
          ]
        },
        {
          "id": "kak-dei-stvovat-pri-zaprose-razresheniya",
          "title": "Как действовать при запросе разрешения",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента."
            },
            {
              "type": "text",
              "html": "Для одной безопасной команды выберите разовое Yes, proceed."
            },
            {
              "type": "text",
              "html": "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
            }
          ]
        },
        {
          "id": "chto-dolzhno-poluchitsya",
          "title": "Что должно получиться",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "VTuber отправляет запрос на :8642, а не напрямую на :11434.",
                "Gateway логирует request_id и backend/model без prompt.",
                "Ответ возвращается в UI.",
                "Source tree VTuber чистый."
              ]
            }
          ]
        },
        {
          "id": "tipovye-oshibki-i-dei-stviya",
          "title": "Типовые ошибки и действия",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Connection refused 8642: запустить gateway и проверить /health.",
                "401: локально сверить token; не печатать его.",
                "Model denied: добавить модель в allowlist отдельным конфигурационным шагом.",
                "Требуется source patch: остановиться и зафиксировать contract gap."
              ]
            }
          ]
        },
        {
          "id": "artefakty-i-dokazatelstva",
          "title": "Артефакты и доказательства",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Обезличенные выбранные keys.",
                "Gateway request_id.",
                "Text-only ответ.",
                "git status --short обоих проектов."
              ]
            }
          ]
        },
        {
          "id": "kriterii-zaversheniya",
          "title": "Критерий завершения",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Единый endpoint работает.",
                "Cloud не вызывается.",
                "VTuber source/templates/dependencies неизменны."
              ]
            },
            {
              "type": "note",
              "html": "<strong>ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Эксперимент 10: Интеграция gateway с Open-LLM-VTuber | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Проверить vision и voice path через единый gateway."
                }
              ]
            }
          ]
        }
      ],
      "summary": "Атомарная цель. Перенаправить LLM-трафик на 127.0.0.1:8642 только локальной конфигурацией.",
      "words": 494,
      "estimate_minutes": 4,
      "task": {
        "objective": [
          "Интеграция должна доказать, что приложение воспринимает gateway как обычный OpenAI-compatible provider. Если для этого приходится менять factory или templates, граница совместимости не выдержана и эксперимент останавливается до отдельного adapter-шага."
        ],
        "prerequisites": [
          "Gateway health зелёный.",
          "Ollama работает.",
          "Известен локальный gateway token, но он не выводится и не сохраняется в evidence."
        ],
        "where": [
          "Три процесса в отдельных окнах: Ollama, gateway, Open-LLM-VTuber. Codex меняет только local conf.yaml после подтверждения."
        ],
        "scope": [
          "Запрещено — Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок.",
          "Результат — Артефакты и критерии на третьей странице эксперимента.",
          "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения.",
          "PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ"
        ],
        "commands": [
          {
            "language": "text",
            "text": "# Окно 1\nollama serve\n\n# Окно 2\nSet-Location C:\\dev\\agent-lab\\local-agent-gateway\nuv run uvicorn app.main:app --host 127.0.0.1 --port 8642\n\n# Окно 3\nSet-Location C:\\dev\\agent-lab\\Open-LLM-VTuber\nuv run run_server.py --verbose"
          }
        ],
        "command_notes": [
          "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее.",
          "КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ"
        ],
        "permissions": [
          "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента.",
          "Для одной безопасной команды выберите разовое Yes, proceed.",
          "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
        ],
        "expected": [
          "VTuber отправляет запрос на :8642, а не напрямую на :11434.",
          "Gateway логирует request_id и backend/model без prompt.",
          "Ответ возвращается в UI.",
          "Source tree VTuber чистый."
        ],
        "pitfalls": [
          "Connection refused 8642: запустить gateway и проверить /health.",
          "401: локально сверить token; не печатать его.",
          "Model denied: добавить модель в allowlist отдельным конфигурационным шагом.",
          "Требуется source patch: остановиться и зафиксировать contract gap."
        ],
        "artifacts": [
          "Обезличенные выбранные keys.",
          "Gateway request_id.",
          "Text-only ответ.",
          "git status --short обоих проектов."
        ],
        "done_when": [
          "Единый endpoint работает.",
          "Cloud не вызывается.",
          "VTuber source/templates/dependencies неизменны.",
          "ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ"
        ],
        "snippets": [
          {
            "section": "Scope",
            "language": "text",
            "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nТекущая задача: изменить только локальный untracked conf.yaml Open-LLM-VTuber. Не меняй source, templates и dependencies.\n\nСначала найди точные keys openai_compatible_llm и basic_memory_agent.llm_provider. Покажи plan для llm_provider: openai_compatible_llm, base_url: http://127.0.0.1:8642/v1, model: qwen3-vl:4b и llm_api_key: <локальный token>. Не показывай реальное значение token ни до, ни после изменения.\n\nПосле подтверждения измени только эти keys. Запусти verbose server и сделай один text-only smoke test. Сопоставь request_id приложения и gateway. Покажи git status --short."
          },
          {
            "section": "Команды PowerShell",
            "language": "text",
            "text": "# Окно 1\nollama serve\n\n# Окно 2\nSet-Location C:\\dev\\agent-lab\\local-agent-gateway\nuv run uvicorn app.main:app --host 127.0.0.1 --port 8642\n\n# Окно 3\nSet-Location C:\\dev\\agent-lab\\Open-LLM-VTuber\nuv run run_server.py --verbose"
          },
          {
            "section": "Критерий завершения",
            "language": "text",
            "text": "Эксперимент 10: Интеграция gateway с Open-LLM-VTuber | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Проверить vision и voice path через единый gateway."
          }
        ]
      }
    },
    {
      "id": "experiment-11",
      "title": "Эксперимент 11. Экран и голос через gateway",
      "kind": "experiment",
      "number": "11",
      "order": 14,
      "sections": [
        {
          "id": "section-1",
          "title": "",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Атомарная цель. </strong>Проверить один vision-запрос и один локальный voice cycle с контролем приватности и feedback loop."
                }
              ]
            }
          ]
        },
        {
          "id": "chto-my-delaem-i-zachem",
          "title": "Что мы делаем и зачем",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Text-only тест не доказывает перенос изображения, а voice baseline не доказывает работу через gateway. Здесь связываются все компоненты: screenshot -&gt; VLM через gateway -&gt; ответ -&gt; локальный TTS, при этом аудио остаётся локальным."
            }
          ]
        },
        {
          "id": "chto-dolzhno-byt-gotovo",
          "title": "Что должно быть готово",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Эксперимент 10 PASS.",
                "Подготовлен безопасный screenshot без имён, чатов, token и cookies.",
                "Динамики на умеренной громкости."
              ]
            }
          ]
        },
        {
          "id": "gde-vypolnyat",
          "title": "Где выполнять",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Те же три окна процессов и браузер localhost. Логи собираются обезличенно."
            }
          ]
        },
        {
          "id": "scope",
          "title": "Scope",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Разрешено</strong>",
                "Только действия, прямо указанные в prompt и командах этого эксперимента."
              ],
              "rows": [
                [
                  "<strong>Запрещено</strong>",
                  "Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок."
                ],
                [
                  "<strong>Результат</strong>",
                  "Артефакты и критерии на третьей странице эксперимента."
                ]
              ]
            },
            {
              "type": "text",
              "html": "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения."
            },
            {
              "type": "note",
              "html": "<strong>PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Проведи два ограниченных smoke test без изменения файлов.\n\nТест A: отправь приложенный обезличенный screenshot в Open-LLM-VTuber и задай короткий вопрос о видимом объекте. Докажи по request_id, что payload прошёл через gateway к локальному qwen3-vl:4b.\n\nТест B: один раз включи microphone capture и попроси меня произнести «Шлюз локального голоса 7319». В логах сохрани только метку [REDACTED SPEECH] и уникальное число 7319. После одного TTS-ответа попроси меня выключить микрофон и 15 секунд наблюдай отсутствие новых циклов.\n\nНе отправляй аудио в cloud API. Не сохраняй исходный audio. Не выводи полные prompt/response. Ничего не изменяй. В конце дай PASS/FAIL отдельно для vision, ASR, gateway, TTS и loop control."
                }
              ]
            }
          ]
        },
        {
          "id": "komandy-powershell",
          "title": "Команды PowerShell",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее."
            },
            {
              "type": "note",
              "html": "<strong>КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "# Процессы уже запущены. Read-only проверки:\nInvoke-RestMethod http://127.0.0.1:8642/health\nGet-NetTCPConnection -LocalPort 11434,8642,12393 -State Listen\ngit status --short"
                }
              ]
            }
          ]
        },
        {
          "id": "kak-dei-stvovat-pri-zaprose-razresheniya",
          "title": "Как действовать при запросе разрешения",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента."
            },
            {
              "type": "text",
              "html": "Для одной безопасной команды выберите разовое Yes, proceed."
            },
            {
              "type": "text",
              "html": "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
            }
          ]
        },
        {
          "id": "chto-dolzhno-poluchitsya",
          "title": "Что должно получиться",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Vision-ответ относится к содержимому screenshot.",
                "Request id связывает VTuber и gateway.",
                "ASR transcript сохраняет число 7319.",
                "TTS слышен локально; после выключения микрофона нет нового request_id."
              ]
            }
          ]
        },
        {
          "id": "tipovye-oshibki-i-dei-stviya",
          "title": "Типовые ошибки и действия",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Screenshot содержит PII: заменить безопасным тестовым изображением.",
                "Vision payload отсутствует: проверить frontend message type read-only.",
                "Повторные циклы: немедленно выключить capture и классифицировать VAD/echo control.",
                "Audio ушёл в cloud: FAIL и остановка эксперимента."
              ]
            }
          ]
        },
        {
          "id": "artefakty-i-dokazatelstva",
          "title": "Артефакты и доказательства",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Безопасный screenshot.",
                "Request id vision.",
                "Transcript с 7319.",
                "Ручное подтверждение TTS.",
                "15-секундное наблюдение loop."
              ]
            }
          ]
        },
        {
          "id": "kriterii-zaversheniya",
          "title": "Критерий завершения",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Vision, ASR, gateway и TTS имеют отдельные доказательства.",
                "Аудио не покидало машину.",
                "Нет feedback loop."
              ]
            },
            {
              "type": "note",
              "html": "<strong>ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Эксперимент 11: Экран и голос через gateway | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Проверить, нужен ли вообще adapter между VTuber и gateway."
                }
              ]
            }
          ]
        }
      ],
      "summary": "Атомарная цель. Проверить один vision-запрос и один локальный voice cycle с контролем приватности и feedback loop.",
      "words": 428,
      "estimate_minutes": 3,
      "task": {
        "objective": [
          "Text-only тест не доказывает перенос изображения, а voice baseline не доказывает работу через gateway. Здесь связываются все компоненты: screenshot -&gt; VLM через gateway -&gt; ответ -&gt; локальный TTS, при этом аудио остаётся локальным."
        ],
        "prerequisites": [
          "Эксперимент 10 PASS.",
          "Подготовлен безопасный screenshot без имён, чатов, token и cookies.",
          "Динамики на умеренной громкости."
        ],
        "where": [
          "Те же три окна процессов и браузер localhost. Логи собираются обезличенно."
        ],
        "scope": [
          "Запрещено — Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок.",
          "Результат — Артефакты и критерии на третьей странице эксперимента.",
          "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения.",
          "PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ"
        ],
        "commands": [
          {
            "language": "text",
            "text": "# Процессы уже запущены. Read-only проверки:\nInvoke-RestMethod http://127.0.0.1:8642/health\nGet-NetTCPConnection -LocalPort 11434,8642,12393 -State Listen\ngit status --short"
          }
        ],
        "command_notes": [
          "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее.",
          "КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ"
        ],
        "permissions": [
          "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента.",
          "Для одной безопасной команды выберите разовое Yes, proceed.",
          "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
        ],
        "expected": [
          "Vision-ответ относится к содержимому screenshot.",
          "Request id связывает VTuber и gateway.",
          "ASR transcript сохраняет число 7319.",
          "TTS слышен локально; после выключения микрофона нет нового request_id."
        ],
        "pitfalls": [
          "Screenshot содержит PII: заменить безопасным тестовым изображением.",
          "Vision payload отсутствует: проверить frontend message type read-only.",
          "Повторные циклы: немедленно выключить capture и классифицировать VAD/echo control.",
          "Audio ушёл в cloud: FAIL и остановка эксперимента."
        ],
        "artifacts": [
          "Безопасный screenshot.",
          "Request id vision.",
          "Transcript с 7319.",
          "Ручное подтверждение TTS.",
          "15-секундное наблюдение loop."
        ],
        "done_when": [
          "Vision, ASR, gateway и TTS имеют отдельные доказательства.",
          "Аудио не покидало машину.",
          "Нет feedback loop.",
          "ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ"
        ],
        "snippets": [
          {
            "section": "Scope",
            "language": "text",
            "text": "Проведи два ограниченных smoke test без изменения файлов.\n\nТест A: отправь приложенный обезличенный screenshot в Open-LLM-VTuber и задай короткий вопрос о видимом объекте. Докажи по request_id, что payload прошёл через gateway к локальному qwen3-vl:4b.\n\nТест B: один раз включи microphone capture и попроси меня произнести «Шлюз локального голоса 7319». В логах сохрани только метку [REDACTED SPEECH] и уникальное число 7319. После одного TTS-ответа попроси меня выключить микрофон и 15 секунд наблюдай отсутствие новых циклов.\n\nНе отправляй аудио в cloud API. Не сохраняй исходный audio. Не выводи полные prompt/response. Ничего не изменяй. В конце дай PASS/FAIL отдельно для vision, ASR, gateway, TTS и loop control."
          },
          {
            "section": "Команды PowerShell",
            "language": "text",
            "text": "# Процессы уже запущены. Read-only проверки:\nInvoke-RestMethod http://127.0.0.1:8642/health\nGet-NetTCPConnection -LocalPort 11434,8642,12393 -State Listen\ngit status --short"
          },
          {
            "section": "Критерий завершения",
            "language": "text",
            "text": "Эксперимент 11: Экран и голос через gateway | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Проверить, нужен ли вообще adapter между VTuber и gateway."
          }
        ]
      }
    },
    {
      "id": "experiment-12",
      "title": "Эксперимент 12. Adapter только при доказанном разрыве",
      "kind": "experiment",
      "number": "12",
      "order": 15,
      "sections": [
        {
          "id": "section-1",
          "title": "",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Атомарная цель. </strong>Создать минимальный adapter лишь при наличии воспроизводимого несовместимого request/response контракта."
                }
              ]
            }
          ]
        },
        {
          "id": "chto-my-delaem-i-zachem",
          "title": "Что мы делаем и зачем",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Speculative adapter увеличивает код и скрывает реальную совместимость. Правильный результат эксперимента может быть NOT REQUIRED. Код разрешается только после failing contract test, который показывает конкретное несовпадающее поле."
            }
          ]
        },
        {
          "id": "chto-dolzhno-byt-gotovo",
          "title": "Что должно быть готово",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Есть sanitized request/response наблюдение эксперимента 11.",
                "Gateway tests зелёные."
              ]
            }
          ]
        },
        {
          "id": "gde-vypolnyat",
          "title": "Где выполнять",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Только local-agent-gateway. Open-LLM-VTuber остаётся read-only."
            }
          ]
        },
        {
          "id": "scope",
          "title": "Scope",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Разрешено</strong>",
                "Только действия, прямо указанные в prompt и командах этого эксперимента."
              ],
              "rows": [
                [
                  "<strong>Запрещено</strong>",
                  "Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок."
                ],
                [
                  "<strong>Результат</strong>",
                  "Артефакты и критерии на третьей странице эксперимента."
                ]
              ]
            },
            {
              "type": "text",
              "html": "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения."
            },
            {
              "type": "note",
              "html": "<strong>PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nТекущая задача: определить, нужен ли adapter для совместимости Open-LLM-VTuber с gateway.\n\nСначала read-only сравни фактический sanitized request/response с поддерживаемой схемой gateway. Если формат совместим, не меняй код: зафиксируй NOT REQUIRED и покажи доказательство.\n\nЕсли несовместимость доказана, сначала добавь ровно один failing contract test. Покажи его реальный FAIL. После нового подтверждения реализуй минимальное преобразование только несовместимых полей, сохрани request_id/status и нейтральные ошибки. Затем покажи diff и PASS того же теста плюс полного pytest. Не добавляй features сверх контракта."
                }
              ]
            }
          ]
        },
        {
          "id": "komandy-powershell",
          "title": "Команды PowerShell",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее."
            },
            {
              "type": "note",
              "html": "<strong>КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "powershell",
                  "text": "Set-Location C:\\dev\\agent-lab\\local-agent-gateway\nuv run pytest -q\ngit diff --check\ngit status --short"
                }
              ]
            }
          ]
        },
        {
          "id": "kak-dei-stvovat-pri-zaprose-razresheniya",
          "title": "Как действовать при запросе разрешения",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента."
            },
            {
              "type": "text",
              "html": "Для одной безопасной команды выберите разовое Yes, proceed."
            },
            {
              "type": "text",
              "html": "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
            }
          ]
        },
        {
          "id": "chto-dolzhno-poluchitsya",
          "title": "Что должно получиться",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Совместимый формат приводит к NOT REQUIRED без diff.",
                "При разрыве существует тест, который сначала падает, затем проходит.",
                "Adapter не переписывает prompt и не раскрывает token."
              ]
            }
          ]
        },
        {
          "id": "tipovye-oshibki-i-dei-stviya",
          "title": "Типовые ошибки и действия",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Нет sanitized payload: статус NOT PROVEN, код не писать.",
                "Тест проходит до реализации: он не доказывает разрыв.",
                "Adapter меняет много полей: сузить scope.",
                "VTuber source предлагается править: остановиться."
              ]
            }
          ]
        },
        {
          "id": "artefakty-i-dokazatelstva",
          "title": "Артефакты и доказательства",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "NOT REQUIRED с доказательством либо failing/passing test pair.",
                "Минимальный diff adapter.",
                "Полный pytest output."
              ]
            }
          ]
        },
        {
          "id": "kriterii-zaversheniya",
          "title": "Критерий завершения",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Нет speculative code.",
                "Совместимость доказана тестом.",
                "Open-LLM-VTuber source неизменен."
              ]
            },
            {
              "type": "note",
              "html": "<strong>ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Эксперимент 12: Adapter только при доказанном разрыве | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Перейти к контролируемой инъекции отказов gateway."
                }
              ]
            }
          ]
        }
      ],
      "summary": "Атомарная цель. Создать минимальный adapter лишь при наличии воспроизводимого несовместимого request/response контракта.",
      "words": 467,
      "estimate_minutes": 3,
      "task": {
        "objective": [
          "Speculative adapter увеличивает код и скрывает реальную совместимость. Правильный результат эксперимента может быть NOT REQUIRED. Код разрешается только после failing contract test, который показывает конкретное несовпадающее поле."
        ],
        "prerequisites": [
          "Есть sanitized request/response наблюдение эксперимента 11.",
          "Gateway tests зелёные."
        ],
        "where": [
          "Только local-agent-gateway. Open-LLM-VTuber остаётся read-only."
        ],
        "scope": [
          "Запрещено — Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок.",
          "Результат — Артефакты и критерии на третьей странице эксперимента.",
          "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения.",
          "PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ"
        ],
        "commands": [
          {
            "language": "powershell",
            "text": "Set-Location C:\\dev\\agent-lab\\local-agent-gateway\nuv run pytest -q\ngit diff --check\ngit status --short"
          }
        ],
        "command_notes": [
          "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее.",
          "КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ"
        ],
        "permissions": [
          "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента.",
          "Для одной безопасной команды выберите разовое Yes, proceed.",
          "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
        ],
        "expected": [
          "Совместимый формат приводит к NOT REQUIRED без diff.",
          "При разрыве существует тест, который сначала падает, затем проходит.",
          "Adapter не переписывает prompt и не раскрывает token."
        ],
        "pitfalls": [
          "Нет sanitized payload: статус NOT PROVEN, код не писать.",
          "Тест проходит до реализации: он не доказывает разрыв.",
          "Adapter меняет много полей: сузить scope.",
          "VTuber source предлагается править: остановиться."
        ],
        "artifacts": [
          "NOT REQUIRED с доказательством либо failing/passing test pair.",
          "Минимальный diff adapter.",
          "Полный pytest output."
        ],
        "done_when": [
          "Нет speculative code.",
          "Совместимость доказана тестом.",
          "Open-LLM-VTuber source неизменен.",
          "ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ"
        ],
        "snippets": [
          {
            "section": "Scope",
            "language": "text",
            "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nТекущая задача: определить, нужен ли adapter для совместимости Open-LLM-VTuber с gateway.\n\nСначала read-only сравни фактический sanitized request/response с поддерживаемой схемой gateway. Если формат совместим, не меняй код: зафиксируй NOT REQUIRED и покажи доказательство.\n\nЕсли несовместимость доказана, сначала добавь ровно один failing contract test. Покажи его реальный FAIL. После нового подтверждения реализуй минимальное преобразование только несовместимых полей, сохрани request_id/status и нейтральные ошибки. Затем покажи diff и PASS того же теста плюс полного pytest. Не добавляй features сверх контракта."
          },
          {
            "section": "Команды PowerShell",
            "language": "powershell",
            "text": "Set-Location C:\\dev\\agent-lab\\local-agent-gateway\nuv run pytest -q\ngit diff --check\ngit status --short"
          },
          {
            "section": "Критерий завершения",
            "language": "text",
            "text": "Эксперимент 12: Adapter только при доказанном разрыве | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Перейти к контролируемой инъекции отказов gateway."
          }
        ]
      }
    },
    {
      "id": "experiment-13",
      "title": "Эксперимент 13. Инъекция отказов",
      "kind": "experiment",
      "number": "13",
      "order": 16,
      "sections": [
        {
          "id": "section-1",
          "title": "",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Атомарная цель. </strong>Доказать безопасное поведение gateway при неверном token, запрещённой модели, недоступном Ollama и timeout."
                }
              ]
            }
          ]
        },
        {
          "id": "chto-my-delaem-i-zachem",
          "title": "Что мы делаем и зачем",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Happy-path недостаточен для runtime-сервиса. Ошибка upstream не должна превращаться в stack trace, зависание или утечку секретов. Каждый отказ проверяется отдельным автоматическим тестом с ожидаемым HTTP status."
            }
          ]
        },
        {
          "id": "chto-dolzhno-byt-gotovo",
          "title": "Что должно быть готово",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Gateway unit/contract tests зелёные.",
                "Ошибки можно симулировать mock transport без остановки пользовательских процессов."
              ]
            }
          ]
        },
        {
          "id": "gde-vypolnyat",
          "title": "Где выполнять",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "local-agent-gateway, преимущественно tests/. Реальный Ollama останавливать не требуется, если отказ корректно мокируется."
            }
          ]
        },
        {
          "id": "scope",
          "title": "Scope",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Разрешено</strong>",
                "Только действия, прямо указанные в prompt и командах этого эксперимента."
              ],
              "rows": [
                [
                  "<strong>Запрещено</strong>",
                  "Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок."
                ],
                [
                  "<strong>Результат</strong>",
                  "Артефакты и критерии на третьей странице эксперимента."
                ]
              ]
            },
            {
              "type": "text",
              "html": "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения."
            },
            {
              "type": "note",
              "html": "<strong>PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nДобавь только тесты безопасных отказов gateway: отсутствующий/неверный Bearer -> 401; запрещённый backend/model -> отказ до network call; upstream connection failure -> 502 или 503; timeout -> 504 или документированный нейтральный статус. Проверяй отсутствие token, prompt, traceback и локальных путей в body/log capture.\n\nСначала покажи план тестов и ожидаемую таблицу status. После подтверждения добавь тесты. Меняй production code только если реальный тест обнаружит несоответствие, и только после отдельного подтверждения. Покажи полный pytest output и git diff."
                }
              ]
            }
          ]
        },
        {
          "id": "komandy-powershell",
          "title": "Команды PowerShell",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее."
            },
            {
              "type": "note",
              "html": "<strong>КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "powershell",
                  "text": "Set-Location C:\\dev\\agent-lab\\local-agent-gateway\nuv run pytest -q\ngit diff --check\ngit status --short"
                }
              ]
            }
          ]
        },
        {
          "id": "kak-dei-stvovat-pri-zaprose-razresheniya",
          "title": "Как действовать при запросе разрешения",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента."
            },
            {
              "type": "text",
              "html": "Для одной безопасной команды выберите разовое Yes, proceed."
            },
            {
              "type": "text",
              "html": "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
            }
          ]
        },
        {
          "id": "chto-dolzhno-poluchitsya",
          "title": "Что должно получиться",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Каждый fault имеет отдельный тест.",
                "HTTP body нейтрален.",
                "После симулированного отказа health endpoint продолжает отвечать."
              ]
            }
          ]
        },
        {
          "id": "tipovye-oshibki-i-dei-stviya",
          "title": "Типовые ошибки и действия",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Тест зависит от реальной сети: заменить mock transport.",
                "Stack trace попал в body: исправить exception mapping отдельным минимальным diff.",
                "Token виден в captured log: добавить redaction и regression test."
              ]
            }
          ]
        },
        {
          "id": "artefakty-i-dokazatelstva",
          "title": "Артефакты и доказательства",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Матрица fault/status.",
                "Полный pytest output.",
                "Проверка отсутствия секретов."
              ]
            }
          ]
        },
        {
          "id": "kriterii-zaversheniya",
          "title": "Критерий завершения",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Все fault tests PASS.",
                "Процесс остаётся работоспособным.",
                "Нет утечек и недокументированных статусов."
              ]
            },
            {
              "type": "note",
              "html": "<strong>ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Эксперимент 13: Инъекция отказов | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Описать проверенный операционный runbook."
                }
              ]
            }
          ]
        }
      ],
      "summary": "Атомарная цель. Доказать безопасное поведение gateway при неверном token, запрещённой модели, недоступном Ollama и timeout.",
      "words": 461,
      "estimate_minutes": 3,
      "task": {
        "objective": [
          "Happy-path недостаточен для runtime-сервиса. Ошибка upstream не должна превращаться в stack trace, зависание или утечку секретов. Каждый отказ проверяется отдельным автоматическим тестом с ожидаемым HTTP status."
        ],
        "prerequisites": [
          "Gateway unit/contract tests зелёные.",
          "Ошибки можно симулировать mock transport без остановки пользовательских процессов."
        ],
        "where": [
          "local-agent-gateway, преимущественно tests/. Реальный Ollama останавливать не требуется, если отказ корректно мокируется."
        ],
        "scope": [
          "Запрещено — Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок.",
          "Результат — Артефакты и критерии на третьей странице эксперимента.",
          "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения.",
          "PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ"
        ],
        "commands": [
          {
            "language": "powershell",
            "text": "Set-Location C:\\dev\\agent-lab\\local-agent-gateway\nuv run pytest -q\ngit diff --check\ngit status --short"
          }
        ],
        "command_notes": [
          "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее.",
          "КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ"
        ],
        "permissions": [
          "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента.",
          "Для одной безопасной команды выберите разовое Yes, proceed.",
          "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
        ],
        "expected": [
          "Каждый fault имеет отдельный тест.",
          "HTTP body нейтрален.",
          "После симулированного отказа health endpoint продолжает отвечать."
        ],
        "pitfalls": [
          "Тест зависит от реальной сети: заменить mock transport.",
          "Stack trace попал в body: исправить exception mapping отдельным минимальным diff.",
          "Token виден в captured log: добавить redaction и regression test."
        ],
        "artifacts": [
          "Матрица fault/status.",
          "Полный pytest output.",
          "Проверка отсутствия секретов."
        ],
        "done_when": [
          "Все fault tests PASS.",
          "Процесс остаётся работоспособным.",
          "Нет утечек и недокументированных статусов.",
          "ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ"
        ],
        "snippets": [
          {
            "section": "Scope",
            "language": "text",
            "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nДобавь только тесты безопасных отказов gateway: отсутствующий/неверный Bearer -> 401; запрещённый backend/model -> отказ до network call; upstream connection failure -> 502 или 503; timeout -> 504 или документированный нейтральный статус. Проверяй отсутствие token, prompt, traceback и локальных путей в body/log capture.\n\nСначала покажи план тестов и ожидаемую таблицу status. После подтверждения добавь тесты. Меняй production code только если реальный тест обнаружит несоответствие, и только после отдельного подтверждения. Покажи полный pytest output и git diff."
          },
          {
            "section": "Команды PowerShell",
            "language": "powershell",
            "text": "Set-Location C:\\dev\\agent-lab\\local-agent-gateway\nuv run pytest -q\ngit diff --check\ngit status --short"
          },
          {
            "section": "Критерий завершения",
            "language": "text",
            "text": "Эксперимент 13: Инъекция отказов | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Описать проверенный операционный runbook."
          }
        ]
      }
    },
    {
      "id": "experiment-14",
      "title": "Эксперимент 14. Операционный runbook",
      "kind": "experiment",
      "number": "14",
      "order": 17,
      "sections": [
        {
          "id": "section-1",
          "title": "",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Атомарная цель. </strong>Создать подробную инструкцию запуска, остановки, health-проверки, диагностики и отката."
                }
              ]
            }
          ]
        },
        {
          "id": "chto-my-delaem-i-zachem",
          "title": "Что мы делаем и зачем",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Runbook превращает разовую демонстрацию в воспроизводимую систему. В него попадают только команды, которые реально проверялись в предыдущих экспериментах. Секреты заменяются placeholders, а интерактивные действия явно отделяются от автоматических."
            }
          ]
        },
        {
          "id": "chto-dolzhno-byt-gotovo",
          "title": "Что должно быть готово",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Известны рабочие порты и команды.",
                "Fault matrix завершена.",
                "Есть фактические логи запуска."
              ]
            }
          ]
        },
        {
          "id": "gde-vypolnyat",
          "title": "Где выполнять",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Только README.md проекта local-agent-gateway. Документация Open-LLM-VTuber не меняется."
            }
          ]
        },
        {
          "id": "scope",
          "title": "Scope",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Разрешено</strong>",
                "Только действия, прямо указанные в prompt и командах этого эксперимента."
              ],
              "rows": [
                [
                  "<strong>Запрещено</strong>",
                  "Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок."
                ],
                [
                  "<strong>Результат</strong>",
                  "Артефакты и критерии на третьей странице эксперимента."
                ]
              ]
            },
            {
              "type": "text",
              "html": "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения."
            },
            {
              "type": "note",
              "html": "<strong>PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nТекущая задача: дополнить только README.md local-agent-gateway проверенным Windows PowerShell runbook.\n\nВключи prerequisites, структуру окон, порядок Ollama -> gateway -> Open-LLM-VTuber, health checks, smoke tests, штатную остановку Ctrl+C, проверку освобождения портов, таблицу симптом/класс/проверка/исправление, rollback local conf.yaml и правила обращения с token. Не вставляй реальные secrets, абсолютный пользовательский путь и непроверенные команды.\n\nСначала покажи оглавление и источники каждой команды. После подтверждения измени README, покажи diff, выполни команды, которые можно безопасно повторить, и полный pytest."
                }
              ]
            }
          ]
        },
        {
          "id": "komandy-powershell",
          "title": "Команды PowerShell",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее."
            },
            {
              "type": "note",
              "html": "<strong>КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "# Старт\nollama serve\nuv run uvicorn app.main:app --host 127.0.0.1 --port 8642\nuv run run_server.py --verbose\n\n# Проверки\nInvoke-RestMethod http://127.0.0.1:8642/health\nGet-NetTCPConnection -LocalPort 11434,8642,12393 -State Listen\n\n# Остановка каждого foreground-процесса: Ctrl+C"
                }
              ]
            }
          ]
        },
        {
          "id": "kak-dei-stvovat-pri-zaprose-razresheniya",
          "title": "Как действовать при запросе разрешения",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента."
            },
            {
              "type": "text",
              "html": "Для одной безопасной команды выберите разовое Yes, proceed."
            },
            {
              "type": "text",
              "html": "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
            }
          ]
        },
        {
          "id": "chto-dolzhno-poluchitsya",
          "title": "Что должно получиться",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Новый пользователь понимает, какое окно для какой команды.",
                "Каждый health check имеет ожидаемый результат.",
                "Rollback не удаляет данные и не меняет upstream history."
              ]
            }
          ]
        },
        {
          "id": "tipovye-oshibki-i-dei-stviya",
          "title": "Типовые ошибки и действия",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "README содержит непроверенную команду: пометить как NOT VERIFIED или удалить.",
                "Вставлен реальный token: немедленно заменить placeholder и проверить Git diff.",
                "Команды зависят от текущей папки: добавить Set-Location перед ними."
              ]
            }
          ]
        },
        {
          "id": "artefakty-i-dokazatelstva",
          "title": "Артефакты и доказательства",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "README diff.",
                "Повторный pytest.",
                "Реальные health outputs.",
                "Таблица диагностики."
              ]
            }
          ]
        },
        {
          "id": "kriterii-zaversheniya",
          "title": "Критерий завершения",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Runbook воспроизводит baseline.",
                "Нет секретов/PII.",
                "Все команды PowerShell-совместимы."
              ]
            },
            {
              "type": "note",
              "html": "<strong>ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Эксперимент 14: Операционный runbook | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Подготовить намеренную поставку в собственный fork."
                }
              ]
            }
          ]
        }
      ],
      "summary": "Атомарная цель. Создать подробную инструкцию запуска, остановки, health-проверки, диагностики и отката.",
      "words": 476,
      "estimate_minutes": 3,
      "task": {
        "objective": [
          "Runbook превращает разовую демонстрацию в воспроизводимую систему. В него попадают только команды, которые реально проверялись в предыдущих экспериментах. Секреты заменяются placeholders, а интерактивные действия явно отделяются от автоматических."
        ],
        "prerequisites": [
          "Известны рабочие порты и команды.",
          "Fault matrix завершена.",
          "Есть фактические логи запуска."
        ],
        "where": [
          "Только README.md проекта local-agent-gateway. Документация Open-LLM-VTuber не меняется."
        ],
        "scope": [
          "Запрещено — Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок.",
          "Результат — Артефакты и критерии на третьей странице эксперимента.",
          "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения.",
          "PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ"
        ],
        "commands": [
          {
            "language": "text",
            "text": "# Старт\nollama serve\nuv run uvicorn app.main:app --host 127.0.0.1 --port 8642\nuv run run_server.py --verbose\n\n# Проверки\nInvoke-RestMethod http://127.0.0.1:8642/health\nGet-NetTCPConnection -LocalPort 11434,8642,12393 -State Listen\n\n# Остановка каждого foreground-процесса: Ctrl+C"
          }
        ],
        "command_notes": [
          "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее.",
          "КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ"
        ],
        "permissions": [
          "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента.",
          "Для одной безопасной команды выберите разовое Yes, proceed.",
          "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
        ],
        "expected": [
          "Новый пользователь понимает, какое окно для какой команды.",
          "Каждый health check имеет ожидаемый результат.",
          "Rollback не удаляет данные и не меняет upstream history."
        ],
        "pitfalls": [
          "README содержит непроверенную команду: пометить как NOT VERIFIED или удалить.",
          "Вставлен реальный token: немедленно заменить placeholder и проверить Git diff.",
          "Команды зависят от текущей папки: добавить Set-Location перед ними."
        ],
        "artifacts": [
          "README diff.",
          "Повторный pytest.",
          "Реальные health outputs.",
          "Таблица диагностики."
        ],
        "done_when": [
          "Runbook воспроизводит baseline.",
          "Нет секретов/PII.",
          "Все команды PowerShell-совместимы.",
          "ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ"
        ],
        "snippets": [
          {
            "section": "Scope",
            "language": "text",
            "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nТекущая задача: дополнить только README.md local-agent-gateway проверенным Windows PowerShell runbook.\n\nВключи prerequisites, структуру окон, порядок Ollama -> gateway -> Open-LLM-VTuber, health checks, smoke tests, штатную остановку Ctrl+C, проверку освобождения портов, таблицу симптом/класс/проверка/исправление, rollback local conf.yaml и правила обращения с token. Не вставляй реальные secrets, абсолютный пользовательский путь и непроверенные команды.\n\nСначала покажи оглавление и источники каждой команды. После подтверждения измени README, покажи diff, выполни команды, которые можно безопасно повторить, и полный pytest."
          },
          {
            "section": "Команды PowerShell",
            "language": "text",
            "text": "# Старт\nollama serve\nuv run uvicorn app.main:app --host 127.0.0.1 --port 8642\nuv run run_server.py --verbose\n\n# Проверки\nInvoke-RestMethod http://127.0.0.1:8642/health\nGet-NetTCPConnection -LocalPort 11434,8642,12393 -State Listen\n\n# Остановка каждого foreground-процесса: Ctrl+C"
          },
          {
            "section": "Критерий завершения",
            "language": "text",
            "text": "Эксперимент 14: Операционный runbook | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Подготовить намеренную поставку в собственный fork."
          }
        ]
      }
    },
    {
      "id": "experiment-15",
      "title": "Эксперимент 15. Поставка в fork",
      "kind": "experiment",
      "number": "15",
      "order": 18,
      "sections": [
        {
          "id": "section-1",
          "title": "",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Атомарная цель. </strong>Проверить scope, зафиксировать gateway и открыть draft PR в собственном fork без изменения upstream main."
                }
              ]
            }
          ]
        },
        {
          "id": "chto-my-delaem-i-zachem",
          "title": "Что мы делаем и зачем",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Финальный этап проверяет инженерную дисциплину: commit должен содержать только согласованный gateway и документацию, тесты должны быть воспроизводимы, а публикация выполняется отдельно после показа точного scope."
            }
          ]
        },
        {
          "id": "chto-dolzhno-byt-gotovo",
          "title": "Что должно быть готово",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Все тесты зелёные.",
                "В Git нет .env, audio, model files и персональных screenshots.",
                "gh auth status успешен."
              ]
            }
          ]
        },
        {
          "id": "gde-vypolnyat",
          "title": "Где выполнять",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "local-agent-gateway. Open-LLM-VTuber публикуется только если там есть отдельно согласованный diff; по умолчанию он остаётся чистым."
            }
          ]
        },
        {
          "id": "scope",
          "title": "Scope",
          "level": 2,
          "blocks": [
            {
              "type": "table",
              "head": [
                "<strong>Разрешено</strong>",
                "Только действия, прямо указанные в prompt и командах этого эксперимента."
              ],
              "rows": [
                [
                  "<strong>Запрещено</strong>",
                  "Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок."
                ],
                [
                  "<strong>Результат</strong>",
                  "Артефакты и критерии на третьей странице эксперимента."
                ]
              ]
            },
            {
              "type": "text",
              "html": "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения."
            },
            {
              "type": "note",
              "html": "<strong>PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nТекущая задача: подготовить поставку local-agent-gateway в собственный GitHub fork.\n\nСначала read-only покажи branch, remotes, git status --short, diff --stat, список untracked и результаты secret/large-file audit. Затем выполни git diff --check и полный pytest. Сформулируй точный commit scope и draft PR title/body.\n\nНе выполняй commit, push и создание PR до моего отдельного подтверждения. После подтверждения сделай один commit, push только рабочей ветки без force и открой draft PR в мой fork. Покажи URL, target/base и финальный git status --short. Не меняй main и upstream."
                }
              ]
            }
          ]
        },
        {
          "id": "komandy-powershell",
          "title": "Команды PowerShell",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее."
            },
            {
              "type": "note",
              "html": "<strong>КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "bash",
                  "text": "git branch --show-current\ngit remote -v\ngit status --short\ngit diff --stat\ngit diff --check\nuv run pytest -q\n# commit/push/PR - только после отдельного подтверждения"
                }
              ]
            }
          ]
        },
        {
          "id": "kak-dei-stvovat-pri-zaprose-razresheniya",
          "title": "Как действовать при запросе разрешения",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента."
            },
            {
              "type": "text",
              "html": "Для одной безопасной команды выберите разовое Yes, proceed."
            },
            {
              "type": "text",
              "html": "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
            }
          ]
        },
        {
          "id": "chto-dolzhno-poluchitsya",
          "title": "Что должно получиться",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Diff содержит только ожидаемые gateway-файлы и README.",
                "pytest и diff --check PASS.",
                "Draft PR target - собственный fork и рабочая base branch.",
                "Нет force push."
              ]
            }
          ]
        },
        {
          "id": "tipovye-oshibki-i-dei-stviya",
          "title": "Типовые ошибки и действия",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "В diff есть .env/audio/model: остановиться и исключить без удаления пользовательских данных.",
                "PR направлен upstream: не создавать/закрывать автоматически; исправить target до публикации.",
                "Рабочая ветка main: остановиться."
              ]
            }
          ]
        },
        {
          "id": "artefakty-i-dokazatelstva",
          "title": "Артефакты и доказательства",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Краткий diff.",
                "Полный test output.",
                "Commit hash.",
                "Draft PR URL и target.",
                "Финальный status."
              ]
            }
          ]
        },
        {
          "id": "kriterii-zaversheniya",
          "title": "Критерий завершения",
          "level": 2,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "CI/локальные тесты зелёные.",
                "Секретов и PII нет.",
                "main/upstream не изменены."
              ]
            },
            {
              "type": "note",
              "html": "<strong>ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Эксперимент 15: Поставка в fork | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Выполнить финальный Definition of Done и сохранить итоговый отчёт."
                }
              ]
            }
          ]
        }
      ],
      "summary": "Атомарная цель. Проверить scope, зафиксировать gateway и открыть draft PR в собственном fork без изменения upstream main.",
      "words": 507,
      "estimate_minutes": 4,
      "task": {
        "objective": [
          "Финальный этап проверяет инженерную дисциплину: commit должен содержать только согласованный gateway и документацию, тесты должны быть воспроизводимы, а публикация выполняется отдельно после показа точного scope."
        ],
        "prerequisites": [
          "Все тесты зелёные.",
          "В Git нет .env, audio, model files и персональных screenshots.",
          "gh auth status успешен."
        ],
        "where": [
          "local-agent-gateway. Open-LLM-VTuber публикуется только если там есть отдельно согласованный diff; по умолчанию он остаётся чистым."
        ],
        "scope": [
          "Запрещено — Секреты, изменение main/upstream, расширение scope и скрытое исправление новых ошибок.",
          "Результат — Артефакты и критерии на третьей странице эксперимента.",
          "Откройте Codex из папки, указанной на предыдущей странице. Для независимого аудита обязательно создайте новую сессию. Скопируйте следующий prompt целиком, не вырезая ограничения.",
          "PROMPT ДЛЯ CODEX - КОПИРОВАТЬ ЦЕЛИКОМ"
        ],
        "commands": [
          {
            "language": "bash",
            "text": "git branch --show-current\ngit remote -v\ngit status --short\ngit diff --stat\ngit diff --check\nuv run pytest -q\n# commit/push/PR - только после отдельного подтверждения"
          }
        ],
        "command_notes": [
          "Команды ниже нужны либо для ручного выполнения, либо для сверки предложения Codex. Строки после комментария «после подтверждения» не запускаются заранее.",
          "КОМАНДЫ - КОПИРОВАТЬ ПО ПОРЯДКУ"
        ],
        "permissions": [
          "Прочитайте команду и рабочую папку; сразу сверьте их со scope этого эксперимента.",
          "Для одной безопасной команды выберите разовое Yes, proceed.",
          "Если команда читает секреты, пишет за пределами scope или меняет main - нажмите Esc и объясните ограничение."
        ],
        "expected": [
          "Diff содержит только ожидаемые gateway-файлы и README.",
          "pytest и diff --check PASS.",
          "Draft PR target - собственный fork и рабочая base branch.",
          "Нет force push."
        ],
        "pitfalls": [
          "В diff есть .env/audio/model: остановиться и исключить без удаления пользовательских данных.",
          "PR направлен upstream: не создавать/закрывать автоматически; исправить target до публикации.",
          "Рабочая ветка main: остановиться."
        ],
        "artifacts": [
          "Краткий diff.",
          "Полный test output.",
          "Commit hash.",
          "Draft PR URL и target.",
          "Финальный status."
        ],
        "done_when": [
          "CI/локальные тесты зелёные.",
          "Секретов и PII нет.",
          "main/upstream не изменены.",
          "ГОТОВАЯ ЗАПИСЬ В ОТЧЁТ"
        ],
        "snippets": [
          {
            "section": "Scope",
            "language": "text",
            "text": "Работай как инженер в режиме контролируемого агента.\nПеред любым изменением покажи карту релевантной части репозитория, одну атомарную цель, файлы чтения и изменения, риски и команды проверки.\nСначала покажи план. Не меняй файлы до моего явного подтверждения.\nНе используй sudo, force push, git reset --hard, git clean -fd и опасные команды удаления.\nНе переключай и не изменяй main.\nНе читай и не печатай .env, токены, SSH-ключи, cookies и keychain.\nНе трогай файлы за пределами указанного scope.\nНе скрывай ошибки и не подменяй тест словесным утверждением.\nПосле изменения покажи краткий diff, реальный минимальный тест и git status --short.\nЕсли нужен больший scope, остановись и запроси новый атомарный шаг.\n\nТекущая задача: подготовить поставку local-agent-gateway в собственный GitHub fork.\n\nСначала read-only покажи branch, remotes, git status --short, diff --stat, список untracked и результаты secret/large-file audit. Затем выполни git diff --check и полный pytest. Сформулируй точный commit scope и draft PR title/body.\n\nНе выполняй commit, push и создание PR до моего отдельного подтверждения. После подтверждения сделай один commit, push только рабочей ветки без force и открой draft PR в мой fork. Покажи URL, target/base и финальный git status --short. Не меняй main и upstream."
          },
          {
            "section": "Команды PowerShell",
            "language": "bash",
            "text": "git branch --show-current\ngit remote -v\ngit status --short\ngit diff --stat\ngit diff --check\nuv run pytest -q\n# commit/push/PR - только после отдельного подтверждения"
          },
          {
            "section": "Критерий завершения",
            "language": "text",
            "text": "Эксперимент 15: Поставка в fork | Статус: PASS / FAIL / NOT PROVEN\nДоказательство: <минимальный обезличенный вывод> | Не доказано: <ограничение>\nSource tree: unchanged / <известный diff> | Следующий шаг: Выполнить финальный Definition of Done и сохранить итоговый отчёт."
          }
        ]
      }
    },
    {
      "id": "appendix",
      "title": "Приложения",
      "kind": "appendix",
      "order": 19,
      "sections": [
        {
          "id": "prilozhenie-a-bystraya-diagnostika-windows",
          "title": "Приложение A. Быстрая диагностика Windows",
          "level": 1,
          "blocks": [
            {
              "type": "text",
              "html": "Используйте команды только для локальной диагностики. Они не исправляют проблему автоматически и не должны заменять экспериментальный prompt."
            },
            {
              "type": "table",
              "head": [
                "<strong>Codex</strong>",
                "codex --version; where.exe codex"
              ],
              "rows": [
                [
                  "<strong>Sandbox</strong>",
                  "Минимальная проверка внутри Codex: Выполни Get-Location. Ничего не изменяй."
                ],
                [
                  "<strong>GitHub</strong>",
                  "gh auth status; при invalid credential - gh auth login -h github.com -p https -w"
                ],
                [
                  "<strong>Ollama</strong>",
                  "ollama --version; ollama list; Test-NetConnection 127.0.0.1 -Port 11434"
                ],
                [
                  "<strong>Gateway</strong>",
                  "Invoke-RestMethod http://127.0.0.1:8642/health"
                ],
                [
                  "<strong>VTuber</strong>",
                  "Get-NetTCPConnection -LocalPort 12393 -State Listen"
                ]
              ]
            }
          ]
        },
        {
          "id": "windows-sandbox-fallback",
          "title": "Windows sandbox fallback",
          "level": 2,
          "blocks": [
            {
              "type": "text",
              "html": "Предпочтителен elevated sandbox. Если его setup не работает из-за политики или отсутствия прав, официальный fallback задаётся в %USERPROFILE%\\.codex\\config.toml. Не дублируйте секцию [windows]."
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "json",
                  "text": "[windows]\nsandbox = \"unelevated\""
                }
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Не используйте full access как постоянное лечение. </strong>Сначала обновите Codex, повторите setup и проверьте официальный unelevated fallback."
                }
              ]
            }
          ]
        },
        {
          "id": "prilozhenie-b-bezopasnoe-obezlichivanie",
          "title": "Приложение B. Безопасное обезличивание",
          "level": 1,
          "blocks": [
            {
              "type": "text",
              "html": "Перед сохранением evidence замените персональные данные, но не меняйте технический смысл ошибки. Всегда храните оригинал локально только если он действительно нужен; в Git помещается только безопасная копия после отдельного решения."
            },
            {
              "type": "table",
              "head": [
                "<strong>Путь</strong>",
                "C:\\Users\\username\\... -&gt; %USERPROFILE%\\..."
              ],
              "rows": [
                [
                  "<strong>Token</strong>",
                  "Удалить всю строку Token, а не рисовать собственную маску."
                ],
                [
                  "<strong>Речь</strong>",
                  "Личную речь -&gt; [REDACTED SPEECH]; уникальное тестовое число можно сохранить."
                ],
                [
                  "<strong>Prompt</strong>",
                  "Сохранять только тестовую формулировку без личного контекста."
                ],
                [
                  "<strong>Screenshot</strong>",
                  "Использовать специально подготовленное окно без чатов, email, cookies и account UI."
                ],
                [
                  "<strong>Request log</strong>",
                  "request_id, backend, model, status, duration; без Authorization и body."
                ]
              ]
            }
          ]
        },
        {
          "id": "minimalnaya-proverka-teksta",
          "title": "Минимальная проверка текста",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "rg -n \"C:\\\\Users\\\\|Token:|Authorization:|Bearer \" .\\evidence\n# Любое совпадение проверяется вручную. Не печатайте найденный секрет в отчёт."
                }
              ]
            }
          ]
        },
        {
          "id": "prilozhenie-c-prompt-sravneniya-dvuh-auditov",
          "title": "Приложение C. Prompt сравнения двух аудитов",
          "level": 1,
          "blocks": [
            {
              "type": "text",
              "html": "Запускайте после того, как оба независимых отчёта уже получены. Сравнение не должно переписывать отчёты и выбирать вывод по стилю ответа."
            },
            {
              "type": "note",
              "html": "<strong>ГОТОВЫЙ PROMPT</strong>"
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Сравни два приложенных независимых аудита Open-LLM-VTuber.\n\nСоздай таблицу:\nТема | Отчёт 1 | Отчёт 2 | Совпадение/расхождение |\nКоманда локальной проверки | Проверенный итог.\n\nНе выбирай утверждение по уверенности формулировки. Каждое расхождение проверь read-only командой по локальным файлам. Если проверить нельзя, поставь NOT PROVEN. Не читай evidence, .env и секреты. Ничего не изменяй. В конце выполни git status --short."
                }
              ]
            }
          ]
        },
        {
          "id": "kogda-audit-schitaetsya-nezavisimym",
          "title": "Когда аудит считается независимым",
          "level": 2,
          "blocks": [
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Новая сессия Codex.",
                "Первый отчёт не передан и запрещено читать evidence.",
                "Формулировка задачи отличается.",
                "Финальные расхождения проверяются командами, а не третьим мнением модели."
              ]
            }
          ]
        },
        {
          "id": "prilozhenie-d-definition-of-done",
          "title": "Приложение D. Definition of Done",
          "level": 1,
          "blocks": [
            {
              "type": "checklist",
              "items": [
                "Эксперименты 1-8 либо повторены, либо подтверждены контрольной точкой 8.5.",
                "Upstream baseline воспроизводим без изменения source/templates/dependency versions.",
                "Ollama qwen3-vl:4b проходит text и безопасный screenshot smoke tests.",
                "sherpa_onnx_asr распознаёт русскую тестовую фразу без cloud key.",
                "pyttsx3_tts воспроизводит слышимый локальный ответ.",
                "После одного voice cycle нет feedback loop.",
                "Gateway слушает только 127.0.0.1:8642 и требует Bearer для /v1.",
                "Allowlist отклоняет запрещённые backend/model до сетевого вызова.",
                "VTuber использует gateway только через local conf.yaml.",
                "Fault tests покрывают auth, deny, connection failure и timeout.",
                "Runbook содержит только реально проверенные PowerShell-команды.",
                "Секреты, PII, audio и большие модели не tracked.",
                "main и upstream не изменены; публикация только в согласованный fork."
              ]
            }
          ]
        },
        {
          "id": "finalnyi-review-prompt",
          "title": "Финальный review prompt",
          "level": 2,
          "blocks": [
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "code",
                  "language": "text",
                  "text": "Проведи read-only review итогов практикума по Definition of Done.\nДля каждого пункта верни PASS / FAIL / NOT PROVEN и ссылку на конкретное доказательство.\nНе считай словесное утверждение тестом. Не читай секреты и PII.\nОтдельно проверь source tree VTuber, gateway auth/allowlist, локальность ASR/TTS,\nотсутствие feedback loop и target PR. Ничего не изменяй."
                }
              ]
            }
          ]
        },
        {
          "id": "prilozhenie-e-istochniki-i-granicy-versii",
          "title": "Приложение E. Источники и границы версии",
          "level": 1,
          "blocks": [
            {
              "type": "text",
              "html": "Команды и ключи Open-LLM-VTuber в этой редакции сверены с локальной версией 1.2.1: README.md, CLAUDE.md, pyproject.toml, config_templates/conf.default.yaml и src/open_llm_vtuber/config_manager/. Перед прохождением на другой версии повторите эксперименты 3-5."
            },
            {
              "type": "list",
              "ordered": false,
              "items": [
                "Codex documentation: https://developers.openai.com/codex/",
                "Codex Windows sandbox: https://developers.openai.com/codex/windows/",
                "OpenAI API reference: https://platform.openai.com/docs/api-reference",
                "Open-LLM-VTuber: https://github.com/Open-LLM-VTuber/Open-LLM-VTuber",
                "Open-LLM-VTuber quick start: https://open-llm-vtuber.github.io/docs/quick-start",
                "Ollama API: https://docs.ollama.com/api",
                "OpenRouter API: https://openrouter.ai/docs/api-reference/overview",
                "FastAPI: https://fastapi.tiangolo.com/",
                "uv: https://docs.astral.sh/uv/",
                "GitHub CLI: https://cli.github.com/manual/"
              ]
            },
            {
              "type": "callout",
              "blocks": [
                {
                  "type": "text",
                  "html": "<strong>Версионность. </strong>Если фактический вывод расходится с документом, не подгоняйте систему под текст. Зафиксируйте расхождение, проверьте установленную версию и обновите только соответствующий атомарный шаг."
                }
              ]
            }
          ]
        }
      ],
      "summary": "Используйте команды только для локальной диагностики. Они не исправляют проблему автоматически и не должны заменять экспериментальный prompt.",
      "words": 451,
      "estimate_minutes": 3
    }
  ],
  "totals": {
    "units": 19,
    "experiments": 16,
    "words": 11512,
    "estimate_minutes": 82
  }
}
