[
  {
    "number": 1,
    "title": "Комната, которая отвечает",
    "group": "Система",
    "units": [
      {
        "kind": "lead",
        "text": "Koobique связывает измерения, модель физического объекта и управляемое действие. Первый проверяемый контур работает в одном кабинете.",
        "y": 137.3555450439453,
        "page": 6,
        "old": 1,
        "id": "A01-001",
        "source_ids": [
          "A01-001"
        ],
        "anchor": "A01-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Человек входит в кабинет. При разрешённой автоматике датчик фиксирует движение, станция проверяет свежесть наблюдений и освещённость, выбирает светильник, отправляет команду релейному узлу и проверяет сообщённый результат. Физический эффект подтверждается отдельно. В голосовом расширении человек говорит «выключи подсветку»: система отличает её от основного освещения и уточняет неоднозначный объект. Локальное управление сохраняется без интернета. При недоступной станции физическая кнопка исполнительного узла продолжает работать в пределах своего питания и локального режима.",
        "y": 188.46142578125,
        "page": 6,
        "old": 1,
        "id": "A01-002",
        "source_ids": [
          "A01-002"
        ],
        "anchor": "A01-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В этом примере уже есть почти вся будущая экосистема: измерение, модель помещения, имена устройств, полномочия пользователя, доставка команды, физический выход, обратная связь и отказоустойчивость. Именно эту цепочку имеет смысл довести до качества, прежде чем добавлять десятки протоколов или рисовать сложную панель управления.",
        "y": 308.7903747558594,
        "page": 6,
        "old": 1,
        "id": "A01-003",
        "source_ids": [
          "A01-003"
        ],
        "anchor": "A01-003",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A01",
        "caption": "Базовый комплект кабинета. Связи логические: устройства и телефон используют обычную локальную сеть. ПК и удалённый канал не являются обязательными участниками локального управления.",
        "y": 404.93798828125,
        "page": 6,
        "old": 1,
        "id": "A01-004",
        "source_ids": [
          "A01-004"
        ],
        "anchor": "A01-004",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Состав первого комплекта",
        "y": 56.314456939697266,
        "page": 7,
        "old": 1,
        "id": "A01-005",
        "source_ids": [
          "A01-005"
        ],
        "anchor": "A01-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Станция Koobique Hub — отдельный Linux-компьютер, преимущественно подключённый к домашнему роутеру по Wi-Fi. На нём работают Rust-ядро, локальное хранилище, службы управления и, когда выбрана подходящая конфигурация, обработка голоса. Ethernet сохраняется как дополнительный интерфейс. Приложение не требует переключать телефон в отдельную сеть станции.",
        "y": 83.33208465576172,
        "page": 7,
        "old": 1,
        "id": "A01-006",
        "source_ids": [
          "A01-006"
        ],
        "anchor": "A01-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Sense — узел освещённости и температуры. Для первого стенда это отладочная плата ESP32-C6 и готовые сенсорные модули. Сенсоры располагаются вне горячего вычислительного корпуса. В дальнейшем температурный и оптический каналы можно разделить, если одной точкой установки не удаётся корректно измерять обе величины.",
        "y": 171.71102905273438,
        "page": 7,
        "old": 1,
        "id": "A01-007",
        "source_ids": [
          "A01-007"
        ],
        "anchor": "A01-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Presence — узел присутствия. Начальный вариант использует PIR, расширенный — PIR и радар. Это не устройство определения личности и не достоверный счётчик людей: система хранит оценку занятости комнаты с понятными ограничениями.",
        "y": 244.11495971679688,
        "page": 7,
        "old": 1,
        "id": "A01-008",
        "source_ids": [
          "A01-008"
        ],
        "anchor": "A01-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Actuator — исполнительный узел с физической кнопкой и реле. Первая нагрузка — низковольтный светильник 12 В. Измерение тока добавляется отдельным этапом. Работа с открытыми сетевыми цепями 230 В не нужна для проверки архитектуры и не входит в предлагаемый учебный стенд.",
        "y": 300.54388427734375,
        "page": 7,
        "old": 1,
        "id": "A01-009",
        "source_ids": [
          "A01-009"
        ],
        "anchor": "A01-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Koobique App — пользовательский клиент; целевое мобильное приложение строится на KMP для Android и iOS. Оно подключает станцию и устройства, показывает состояние, передаёт намерения и может служить голосовым пультом. Клиент обращается к API Core. KQIP описывает отдельную связь Core с узлами; общий формат данных не означает общий набор полномочий. Платформенные библиотеки выбираются после проверки на реальных телефонах.",
        "y": 372.9478454589844,
        "page": 7,
        "old": 1,
        "id": "A01-010",
        "source_ids": [
          "A01-010"
        ],
        "anchor": "A01-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Три понятия, которые нельзя смешивать",
        "y": 452.74200439453125,
        "page": 7,
        "old": 1,
        "id": "A01-011",
        "source_ids": [
          "A01-011"
        ],
        "anchor": "A01-011",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Понятие",
            "Что оно означает",
            "Что не следует из него"
          ],
          [
            "Автономность",
            "Основные функции выполняются без внешних серверов",
            "Что радио работает без роутера или питания"
          ],
          [
            "Собственный протокол",
            "Свои сообщения, состояния, правила совместимости и восстановления",
            "Что нужно заменить TLS собственной криптографией"
          ],
          [
            "Масштабируемость",
            "Новые возможности добавляются без нарушения прежних контрактов",
            "Что первой комнате нужны кластер и микросервисы"
          ]
        ],
        "y": 481.86,
        "page": 7,
        "old": 1,
        "id": "A01-012",
        "source_ids": [
          "A01-012"
        ],
        "anchor": "A01-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Локальная система должна обходиться без облачной регистрации, внешнего DNS, сервера лицензий и CDN для обычного управления. Но удалённый пользователь физически зависит от существования доступного канала до дома. Протокол не может гарантировать доставку через полностью заблокированную сеть. Поэтому локальная работоспособность и доступность удалённого канала проверяются раздельно.",
        "y": 669.7076416015625,
        "page": 7,
        "old": 1,
        "id": "A01-013",
        "source_ids": [
          "A01-013"
        ],
        "anchor": "A01-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Что означает «готово к первому экземпляру»",
        "y": 56.314456939697266,
        "page": 8,
        "old": 1,
        "id": "A01-014",
        "source_ids": [
          "A01-014"
        ],
        "anchor": "A01-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Эта книга задаёт инженерную основу, сравнение деталей, схемы соединений, расчётные примеры и программу проверок. Она не выдаёт предполагаемую совместимость за испытанную. До заказа окончательных панелей необходимо измерить реальные компоненты; до переноса релейного узла с макета на плату — проверить электрическую схему; до использования постоянного автономного управления — прогнать отказные сценарии.",
        "y": 83.33208465576172,
        "page": 8,
        "old": 1,
        "id": "A01-015",
        "source_ids": [
          "A01-015"
        ],
        "anchor": "A01-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В тексте используются три вида утверждений. Документированный факт сопровождается ссылкой вида [14]. Проектное решение описывает предложенную архитектуру Koobique. Расчёт или цель опирается на явно названные исходные данные и ещё требует измерения. Это позволяет менять детали, не разрушая логику системы.",
        "y": 187.68603515625,
        "page": 8,
        "old": 1,
        "id": "A01-016",
        "source_ids": [
          "A01-016"
        ],
        "anchor": "A01-016",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Первое практическое решение",
        "y": 276.35986328125,
        "page": 8,
        "old": 1,
        "id": "A01-017",
        "source_ids": [
          "A01-017"
        ],
        "anchor": "A01-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Не покупать всю станцию сразу. Сначала запустить ту же серверную архитектуру на имеющемся ПК и собрать три низковольтных узла. Отдельный вычислитель выбирается после измерения нагрузки и голоса. Это не упрощённый «одноразовый прототип»: формат сообщений, модель устройств и проверка прав остаются теми же.",
        "y": 294.821044921875,
        "page": 8,
        "old": 1,
        "id": "A01-018",
        "source_ids": [
          "A01-018"
        ],
        "anchor": "A01-018",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Как устроена эта книга",
        "y": 382.0987854003906,
        "page": 8,
        "old": 1,
        "id": "A01-019",
        "edited": true,
        "source_ids": [
          "A01-019"
        ],
        "anchor": "A01-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сначала мы проследим один сценарий кабинета и разберём устройство станции. Затем соберём корпус, питание, измерительные и исполнительные узлы. После этого пройдём программный путь от загрузки Linux и прошивки микроконтроллера к протоколу, данным, моделям и решениям. Приложение, голос, обновления и испытания замыкают эту цепочку.",
        "y": 409.11639404296875,
        "page": 8,
        "old": 1,
        "id": "A01-020",
        "edited": true,
        "source_ids": [
          "A01-020"
        ],
        "anchor": "A01-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для последовательного изучения сохранены все 51 глава. Во время сборки удобно идти по маршруту E0 и возвращаться к монтажным картам, расчётам и проверкам. Разработчику Core/MCU и исследователю данных доступны собственные маршруты через те же полные главы. Учебные источники собраны в конце книги. Проектные размеры и настройки сверяются с приобретёнными деталями перед изготовлением.",
        "y": 497.495361328125,
        "page": 8,
        "old": 1,
        "id": "A01-021",
        "edited": true,
        "source_ids": [
          "A01-021"
        ],
        "anchor": "A01-021",
        "content_status": "active"
      },
      {
        "id": "R01-001",
        "kind": "h2",
        "text": "Один первый результат — без сокращения горизонта проекта",
        "source_ids": [
          "R01-001"
        ],
        "anchor": "R01-001",
        "content_status": "active"
      },
      {
        "id": "R01-002",
        "kind": "p",
        "text": "Профиль E0 — одна комната, один активный Core, один владелец, измерение температуры и света, PIR-only и одна низковольтная лампа. Успех означает: свет можно предсказуемо включить, ручное решение не отменяется само, отказ виден, а после перезапуска есть понятный путь восстановления. Пакеты процессов, голос, радар и AI остаются в книге, но не становятся обязательными зависимостями этого первого результата.",
        "source_ids": [
          "R01-002"
        ],
        "anchor": "R01-002",
        "content_status": "active"
      },
      {
        "id": "R01-003",
        "kind": "table",
        "rows": [
          [
            "Сейчас: E0",
            "Следом: E1",
            "Исследование / E2"
          ],
          [
            "H0 на ПК, низковольтные узлы, ручной режим",
            "Автономный Hub, измеренные OTA и восстановление",
            "Вторая комната без изменения Core"
          ],
          [
            "Статический паспорт процесса и автомат входа",
            "Проверенный PIR + радар; голос с телефона",
            "Модели мира, MPC и обучение — по отдельным метрикам"
          ],
          [
            "Тесты повтора, времени, выхода и отказов",
            "Отзыв телефона, защита ключей, KMP Android/iOS",
            "Собственный корпус и плата по проверенным деталям"
          ]
        ],
        "source_ids": [
          "R01-003"
        ],
        "anchor": "R01-003",
        "content_status": "active"
      },
      {
        "id": "R01-004",
        "kind": "p",
        "text": "«Зафиксировано проектом» в этой редакции означает выбранное поведение, а не проведённое испытание. Нормативные пункты E0, примеры и будущие варианты визуально разделены. Для замены компонента не требуется переписывать всю книгу, но требуется заново пройти затронутую проверку.",
        "source_ids": [
          "R01-004"
        ],
        "anchor": "R01-004",
        "content_status": "active"
      },
      {
        "id": "R3-01-CYCLE-H",
        "kind": "h2",
        "text": "Физический цикл: Sense → State → Model → Decision → Act",
        "source_ids": [],
        "anchor": "R3-01-CYCLE-H",
        "content_status": "r3_addition"
      },
      {
        "id": "R3-01-CYCLE-P",
        "kind": "p",
        "text": "Sense получает наблюдение с временем, единицей и качеством. State сохраняет то, что сейчас известно об объекте, включая пропуски и неопределённость. Model описывает связи и ожидаемый эффект действия. Decision выбирает разрешённое намерение с учётом цели и ограничений. Act проверяет условия, исполняет команду и возвращает результат, который следующий Sense сопоставляет с физическим миром. В E0 некоторые шаги выполняет простой автомат; отдельная нейросеть для каждого шага не требуется.",
        "source_ids": [],
        "anchor": "R3-01-CYCLE-P",
        "content_status": "r3_addition"
      }
    ],
    "origins": [
      "A01"
    ],
    "question": "Как выглядит один законченный цикл работы?",
    "intro": "В обычное утро человек входит в кабинет и садится за стол. Если комнате разрешено автоматическое управление и света недостаточно, система включает нужную лампу. Ручное выключение сохраняет приоритет человека. Потеряв датчик, система сообщает о нехватке данных и сохраняет предусмотренное для этого случая поведение.\n\nВ этом сценарии встречаются электрическая схема, радиосвязь, время, права доступа, человеческое намерение и физический результат. Книга разбирает эти слои по очереди и соединяет их в проверяемый рабочий цикл.",
    "id": "ch-1",
    "slug": "chapter-01"
  },
  {
    "number": 2,
    "title": "Кто за что отвечает",
    "group": "Система",
    "units": [
      {
        "kind": "lead",
        "text": "Устройство отвечает за безопасный выход, станция — за координацию, приложение — за намерение пользователя.",
        "y": 168.675537109375,
        "page": 9,
        "old": 2,
        "id": "A02-001",
        "source_ids": [
          "A02-001"
        ],
        "anchor": "A02-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сервер не должен быть единственным местом, где известно, как выключить реле. Приложение не должно быть единственным местом, где хранится расписание. Датчик не должен решать, какой светильник считается основным в комнате. Такое разделение не только упрощает код: оно ограничивает последствия сбоя каждого узла.",
        "y": 219.5513153076172,
        "page": 9,
        "old": 2,
        "id": "A02-002",
        "source_ids": [
          "A02-002"
        ],
        "anchor": "A02-002",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A02",
        "caption": "Замкнутый цикл. Подтверждение команды и независимое наблюдение физического результата различаются по силе доказательства.",
        "y": 296.2060546875,
        "page": 9,
        "old": 2,
        "id": "A02-003",
        "source_ids": [
          "A02-003"
        ],
        "anchor": "A02-003",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Три уровня исполнения",
        "y": 569.482421875,
        "page": 9,
        "old": 2,
        "id": "A02-004",
        "source_ids": [
          "A02-004"
        ],
        "anchor": "A02-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "На микроконтроллере выполняются опрос датчиков, работа с контактами, ограничение длительности действия, физическая кнопка и заранее согласованный режим потери связи. Сеть может задержаться, но кнопка не должна ждать сетевой mutex. Локальная защита имеет приоритет перед командой сервера.",
        "y": 596.2699584960938,
        "page": 9,
        "old": 2,
        "id": "A02-005",
        "source_ids": [
          "A02-005"
        ],
        "anchor": "A02-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Станция хранит состав комнаты, роли пользователей, автоматизации и историю решений. Она объединяет наблюдения, выбирает действие и следит за выполнением. Linux предоставляет процессы, память, драйверы и сеть; собственная платформа Koobique отвечает за смысл физической системы, а не заменяет ядро Linux.",
        "y": 664.0755004882812,
        "page": 9,
        "old": 2,
        "id": "A02-006",
        "source_ids": [
          "A02-006"
        ],
        "anchor": "A02-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Приложение показывает подтверждённые состояния и отправляет намерения. Закрытие приложения не останавливает правило «при присутствии включать свет, когда темно». Потеря push-уведомления не означает потерю управляющей команды: это разные каналы с разными гарантиями.",
        "y": 57.198036193847656,
        "page": 10,
        "old": 2,
        "id": "A02-007",
        "source_ids": [
          "A02-007"
        ],
        "anchor": "A02-007",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Возможности вместо жёстких классов",
        "y": 130.63967895507812,
        "page": 10,
        "old": 2,
        "id": "A02-008",
        "source_ids": [
          "A02-008"
        ],
        "anchor": "A02-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Устройство описывается набором функциональных каналов. Один узел может иметь temperature, illuminance, button и relay; другой — только occupancy. Для каждого канала нужны тип величины, единица, диапазон, доступные команды, частота или режим обновления, качество данных и версия описания.",
        "y": 157.42721557617188,
        "page": 10,
        "old": 2,
        "id": "A02-009",
        "source_ids": [
          "A02-009"
        ],
        "anchor": "A02-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Так приложение может построить стандартный элемент управления реле, не зная названия конкретной платы. Но дескриптор не получает права прислать исполняемый JavaScript или произвольный код интерфейса. Специальные экраны появляются в приложении как проверенные расширения, а не как содержимое пакета от неизвестного устройства.",
        "y": 225.23275756835938,
        "page": 10,
        "old": 2,
        "id": "A02-010",
        "source_ids": [
          "A02-010"
        ],
        "anchor": "A02-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Полезно разделить идентичность узла, физическое размещение и пользовательское имя. Перенос Sense из кабинета в коридор не создаёт новое устройство, но меняет контекст измерений. Переименование «лампа» в «основной свет» не должно ломать правило, которое ссылается на устойчивый идентификатор канала.",
        "y": 308.0762634277344,
        "page": 10,
        "old": 2,
        "id": "A02-011",
        "source_ids": [
          "A02-011"
        ],
        "anchor": "A02-011",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Слои состояния системы",
        "y": 381.5179138183594,
        "page": 10,
        "old": 2,
        "id": "A02-012",
        "source_ids": [
          "A02-012"
        ],
        "anchor": "A02-012",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Состояние",
            "Пример",
            "Кто является источником"
          ],
          [
            "Желаемое",
            "Основной свет должен быть включён",
            "Пользователь или правило"
          ],
          [
            "Выполнение",
            "Команда принята, отправлена, результат неизвестен",
            "Исполнитель команд станции"
          ],
          [
            "Сообщённое",
            "Прошивка установила выход в 1",
            "Узел Actuator"
          ],
          [
            "Наблюдаемое",
            "По цепи течёт 0,49 А",
            "Измеритель тока"
          ],
          [
            "Оценённое",
            "Рабочее место освещено",
            "Модель, использующая lux и контекст"
          ]
        ],
        "y": 410.63,
        "page": 10,
        "old": 2,
        "id": "A02-013",
        "source_ids": [
          "A02-013"
        ],
        "anchor": "A02-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Эти сущности связаны, но не подменяют друг друга. Ток может течь через неисправную нагрузку. Люксометр может видеть дневной свет, хотя реле не включилось. Поэтому результат имеет уровень подтверждения, а интерфейс не обещает больше, чем известно.",
        "y": 625.4321899414062,
        "page": 10,
        "old": 2,
        "id": "A02-014",
        "source_ids": [
          "A02-014"
        ],
        "anchor": "A02-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Приоритеты и конфликтующие решения",
        "y": 683.8358764648438,
        "page": 10,
        "old": 2,
        "id": "A02-015",
        "source_ids": [
          "A02-015"
        ],
        "anchor": "A02-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Предлагаемый порядок для первого кабинета: аппаратная защита → локальная кнопка и аварийное выключение → явное действие пользователя → обычная автоматизация. Это не вечный порядок для любого оборудования: он является политикой конкретного класса устройства. Например, противопожарная функция или защита от замерзания потребуют своей модели, а не изменения одного глобального числа приоритета.",
        "y": 710.6234130859375,
        "page": 10,
        "old": 2,
        "id": "A02-016",
        "source_ids": [
          "A02-016"
        ],
        "anchor": "A02-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Ручное действие создаёт ManualOverride на исполнительном узле. В E0 режим сохраняется до явного действия «Вернуть автоматику»; обычное правило его не снимает. Временный lease с видимым сроком можно добавить в E1 после отдельного определения часов и поведения при перезапуске. Для будущей опасной нагрузки локальное ограничение максимального времени включения задаётся независимо от ручного режима. Для лампы рабочего стола потеря датчика сама по себе не отменяет явно включённый свет.",
        "y": 57.198036193847656,
        "page": 11,
        "old": 2,
        "id": "A02-017",
        "source_ids": [
          "A02-017"
        ],
        "anchor": "A02-017",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Один процесс ядра, несколько изолированных служб",
        "y": 160.71568298339844,
        "page": 11,
        "old": 2,
        "id": "A02-018",
        "source_ids": [
          "A02-018"
        ],
        "anchor": "A02-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Предлагается модульный Rust-монолит для реестра, состояний, политик и исполнения. Голосовой вывод, распознавание речи и потенциально нестабильные адаптеры работают отдельными процессами. Они получают ограниченные ресурсы и не владеют выходами напрямую.",
        "y": 187.5032196044922,
        "page": 11,
        "old": 2,
        "id": "A02-019",
        "source_ids": [
          "A02-019"
        ],
        "anchor": "A02-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Внутри ядра не нужен сетевой брокер для каждого события. Типизированные вызовы и ограниченные очереди проще проследить и измерить. Отдельный процесс оправдан там, где он ограничивает привилегии, падение или расход памяти. Взаимодействие через локальный сокет не означает, что все модули должны превращаться в микросервисы.",
        "y": 255.3087615966797,
        "page": 11,
        "old": 2,
        "id": "A02-020",
        "source_ids": [
          "A02-020"
        ],
        "anchor": "A02-020",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Когда система растёт",
        "y": 328.7503967285156,
        "page": 11,
        "old": 2,
        "id": "A02-021",
        "source_ids": [
          "A02-021"
        ],
        "anchor": "A02-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Следующие десять устройств добавляются через тот же реестр и протокол. Следующая комната добавляет область действия правил. Второй пользователь — отдельную идентичность и права. Второй хаб не должен автоматически становиться равноправным управляющим тем же реле: сначала требуется протокол передачи полномочий, иначе два восстановленных из копии сервера начнут бороться за выход.",
        "y": 355.53790283203125,
        "page": 11,
        "old": 2,
        "id": "A02-022",
        "source_ids": [
          "A02-022"
        ],
        "anchor": "A02-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первых экземпляров выбираем одну активную станцию на инсталляцию. Резервная копия предназначена для восстановления, а не для создания параллельного хозяина. Управляющий epoch и привязка к владельцу позволяют устройству отвергать старый контроллер после переноса.",
        "y": 438.3814392089844,
        "page": 11,
        "old": 2,
        "id": "A02-023",
        "source_ids": [
          "A02-023"
        ],
        "anchor": "A02-023",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Проверка архитектуры до корпуса",
        "y": 523.5374145507812,
        "page": 11,
        "old": 2,
        "id": "A02-024",
        "source_ids": [
          "A02-024"
        ],
        "anchor": "A02-024",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "На ПК запустить ядро и симулятор Actuator. Отключить клиент: правило продолжает работать. Остановить голосовой worker: ручные команды работают. Оборвать соединение с узлом: результат команды становится неизвестным, а не ложно успешным. Вернуть старый snapshot: он не становится свежим измерением.",
        "y": 541.99853515625,
        "page": 11,
        "old": 2,
        "id": "A02-025",
        "source_ids": [
          "A02-025"
        ],
        "anchor": "A02-025",
        "content_status": "active"
      },
      {
        "kind": "lead",
        "text": "Одна модель не должна одновременно решать, что произошло, чего хочет человек и можно ли включить нагрузку. Эти задачи требуют разных доказательств.",
        "id": "I01-001",
        "old": "I01",
        "source_ids": [
          "I01-001"
        ],
        "anchor": "I01-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Три архитектуры и осознанный выбор",
        "id": "I01-002",
        "old": "I01",
        "source_ids": [
          "I01-002"
        ],
        "anchor": "I01-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Самый короткий путь — отправлять все показания и команды пользователя в языковую модель, а ответ превращать в вызов устройства. Такой прототип впечатляет гибкостью, но связывает физическое действие с качеством текста, полнотой контекста и временем ответа. Отказ одного большого компонента становится отказом всей автоматизации.",
        "id": "I01-003",
        "old": "I01",
        "source_ids": [
          "I01-003"
        ],
        "anchor": "I01-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Противоположный путь — конечные автоматы и жёсткие правила. Он хорош там, где ситуация исчерпывается несколькими состояниями. Для первого реле это необходимая основа. Но каждый новый тип помещения и исключение постепенно увеличивают число правил; понимание произвольной речи и адаптация к неизвестной лампе не появляются сами.",
        "id": "I01-004",
        "old": "I01",
        "source_ids": [
          "I01-004"
        ],
        "anchor": "I01-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для Koobique предлагается гибридная архитектура: проверяемая основа исполняет ограничения и простые сценарии; специализированные модели оценивают скрытое состояние и прогнозируют последствия; языковой слой помогает пользователю выразить цель. Ни одна модель не получает исключительного права определять, что является истинным и разрешённым.",
        "id": "I01-005",
        "old": "I01",
        "source_ids": [
          "I01-005"
        ],
        "anchor": "I01-005",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I01",
        "caption": "Полный цикл. Оранжевый узел — независимая проверка разрешения, а не ещё один запрос к AI. Обратная связь содержит физические наблюдения, а не только ответ прошивки.",
        "id": "I01-006",
        "old": "I01",
        "source_ids": [
          "I01-006"
        ],
        "anchor": "I01-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Что означает «операционная система»",
        "id": "I01-007",
        "old": "I01",
        "source_ids": [
          "I01-007"
        ],
        "anchor": "I01-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Linux остаётся операционной системой вычислителя: загружает драйверы, распределяет память, запускает процессы. Koobique Core / физическая платформа — предлагаемое имя прикладной системной среды над Linux. Она распределяет доступ к физическим объектам, времени измерений, моделям и исполнительным устройствам. Это смысловой аналог ОС для физических процессов, а не заявление о написании собственного планировщика CPU.",
        "id": "I01-008",
        "old": "I01",
        "source_ids": [
          "I01-008"
        ],
        "anchor": "I01-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В обычной ОС файл имеет имя и права. В Koobique физический объект имеет тип величины, расположение, источники наблюдения, допустимые воздействия и условия доверия. Потребность «освещение рабочего места» связывается не с произвольным реле, а с конкретным светильником и проверенным влиянием на выбранную плоскость измерения.",
        "id": "I01-009",
        "old": "I01",
        "source_ids": [
          "I01-009"
        ],
        "anchor": "I01-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Пять разных документов о реальности",
        "id": "I01-010",
        "old": "I01",
        "source_ids": [
          "I01-010"
        ],
        "anchor": "I01-010",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Тип",
            "Пример",
            "Что он не доказывает"
          ],
          [
            "Наблюдение",
            "Датчик сообщил 140 лк",
            "Что весь кабинет одинаково освещён"
          ],
          [
            "Оценка",
            "Вероятно, человек за столом",
            "Что идентифицирован конкретный человек"
          ],
          [
            "Прогноз",
            "После включения ожидается 560 лк",
            "Что лампа исправна сейчас"
          ],
          [
            "Предложение",
            "Включить основное освещение",
            "Что действие разрешено владельцем"
          ],
          [
            "Свидетельство",
            "Свет вырос после команды",
            "Что причинность установлена без альтернатив"
          ]
        ],
        "id": "I01-011",
        "old": "I01",
        "source_ids": [
          "I01-011"
        ],
        "anchor": "I01-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Каждая запись получает собственный тип, происхождение и срок применимости. Оценка может ссылаться на наблюдения, прогноз — на модель и начальное состояние, действие — на разрешение. Преобразование между типами выполняется явно. Нельзя записать предсказанную температуру в канал настоящего датчика.",
        "id": "I01-012",
        "old": "I01",
        "source_ids": [
          "I01-012"
        ],
        "anchor": "I01-012",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Контуры с разной скоростью",
        "id": "I01-013",
        "old": "I01",
        "source_ids": [
          "I01-013"
        ],
        "anchor": "I01-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "На устройстве остаются физическая кнопка, ограничения выхода и реакции на локальный отказ. На станции выполняются межустройственные сценарии, оценка состояния и журнал. Прогнозы и обучение работают медленнее и не блокируют эти задачи. В интерфейсе пользователь видит цель, ограничения и причины; телефон не является обязательным постоянно работающим сервером.",
        "id": "I01-014",
        "old": "I01",
        "source_ids": [
          "I01-014"
        ],
        "anchor": "I01-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Не все задачи требуют миллисекунд. Контроль освещения чувствителен к заметной задержке; тепловой процесс часто меняется гораздо медленнее. Интервал измерения, срок свежести и вычислительный бюджет задаются для каждого процесса, а не глобальным параметром «самая высокая частота».",
        "id": "I01-015",
        "old": "I01",
        "source_ids": [
          "I01-015"
        ],
        "anchor": "I01-015",
        "content_status": "active"
      },
      {
        "kind": "quote",
        "text": "Физический процесс объединяет цель, модель и проверяемые условия исполнения. Замена модели сохраняет идентичность устройств, историю измерений и защитные правила.",
        "id": "I01-016",
        "old": "I01",
        "source_ids": [
          "I01-016"
        ],
        "anchor": "I01-016",
        "content_status": "active"
      },
      {
        "id": "R02-001",
        "kind": "h2",
        "text": "Компоненты системы и области версионирования",
        "source_ids": [
          "R02-001"
        ],
        "anchor": "R02-001",
        "content_status": "active"
      },
      {
        "id": "R02-002",
        "kind": "table",
        "rows": [
          [
            "Термин",
            "Одна роль"
          ],
          [
            "Hub / станция",
            "Физический Linux-компьютер, питание и сеть"
          ],
          [
            "Core",
            "Основная непривилегированная служба Rust"
          ],
          [
            "Sense / Presence / Actuator",
            "Роли MCU-узлов, не названия универсальных протоколов"
          ],
          [
            "boot_id",
            "Случайный 128-битный идентификатор конкретной загрузки MCU"
          ],
          [
            "controller_fence",
            "Назначение управляющего ключа; меняется только процедурой владения"
          ],
          [
            "output_revision",
            "Изменения принятого состояния выхода внутри boot_id"
          ],
          [
            "manual_revision",
            "Ручные намерения и освобождение режима внутри boot_id"
          ],
          [
            "descriptor_revision / placement_revision",
            "Описание канала и его размещение; не права контроллера"
          ]
        ],
        "source_ids": [
          "R02-002"
        ],
        "anchor": "R02-002",
        "content_status": "active"
      },
      {
        "id": "R02-003",
        "kind": "p",
        "text": "Сравнивать две голые ревизии недостаточно. Выход «39» из другой загрузки не продолжает прежнюю последовательность «38». Переименование лампы не меняет её физический канал; перенос датчика меняет размещение; смена хаба меняет доверие. В интерфейсе эти детали скрыты за понятными последствиями, но в журнале они сохраняются.",
        "source_ids": [
          "R02-003"
        ],
        "anchor": "R02-003",
        "content_status": "active"
      }
    ],
    "origins": [
      "A02",
      "I01"
    ],
    "question": "Где проходит граница между Linux, Koobique и устройством?",
    "intro": "Удобно представить три масштаба ответственности. Микроконтроллер находится рядом с контактом реле и способен быстро защитить свой выход. Станция видит несколько устройств и понимает, к какой комнате они относятся. Приложение знает, что попросил человек, но не обязано постоянно работать.\n\nПоверх этого разделения появляется физическая платформа: она связывает измерение с объектом, решение — с целью, а команду — с разрешённым воздействием. Слово «операционная» здесь означает управление общими ресурсами и правилами доступа, а не обещание заново написать Linux.",
    "terms": [
      "worker — отдельный вычислительный помощник",
      "ManualOverride — ручной режим; E0 сохраняет его до явного освобождения, временный lease относится к расширению E1",
      "snapshot — согласованный снимок состояния"
    ],
    "id": "ch-2",
    "slug": "chapter-02"
  },
  {
    "number": 3,
    "title": "На чём запустить Koobique",
    "group": "Вычислитель",
    "units": [
      {
        "kind": "lead",
        "text": "Процессор выбирают по результатам испытания совместной нагрузки. Маркировка «готов к AI» сама по себе не подтверждает пригодность для этого режима.",
        "y": 168.675537109375,
        "page": 12,
        "old": 3,
        "id": "A03-001",
        "edited": true,
        "source_ids": [
          "A03-001"
        ],
        "anchor": "A03-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для температуры и реле не требуется мощная машина. Для локальной речи, акустической обработки, многопользовательского интерфейса и экспериментов с моделями ресурсов нужно больше. Покупать ускоритель до выбора модели рискованно: рекламное число TOPS не отвечает на вопрос, поддерживается ли нужный оператор, формат квантования или runtime.",
        "y": 219.5513153076172,
        "page": 12,
        "old": 3,
        "id": "A03-002",
        "source_ids": [
          "A03-002"
        ],
        "anchor": "A03-002",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A03",
        "caption": "Шесть вариантов H0–H5. Это альтернативы вычислительной основы, а не обязательные этапы покупки.",
        "y": 311.2440490722656,
        "page": 12,
        "old": 3,
        "id": "A03-003",
        "source_ids": [
          "A03-003"
        ],
        "anchor": "A03-003",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "H0. Имеющийся мощный ПК",
        "y": 643.0715942382812,
        "page": 12,
        "old": 3,
        "id": "A03-004",
        "source_ids": [
          "A03-004"
        ],
        "anchor": "A03-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Это лучший первый стенд программной архитектуры. Linux можно установить на отдельный накопитель или использовать отдельную среду разработки; доступ к USB, аудио и Wi-Fi должен быть реальным, а не только эмуляцией. Виртуальная машина полезна для серверной логики, но её сетевые и аудиозадержки нельзя выдавать за характеристики будущего хаба.",
        "y": 669.859130859375,
        "page": 12,
        "old": 3,
        "id": "A03-005",
        "source_ids": [
          "A03-005"
        ],
        "anchor": "A03-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сначала измеряются обычное управление, одновременное распознавание речи и запись истории. Затем нагрузка ограничивается по CPU и памяти, чтобы понять нижнюю границу. Установка лимита не полностью имитирует медленный процессор: остаются отличия кэша, частоты, памяти и ускорителей. Но она быстро выявляет бесконтрольное потребление ресурсов.",
        "y": 57.198036193847656,
        "page": 13,
        "old": 3,
        "id": "A03-006",
        "source_ids": [
          "A03-006"
        ],
        "anchor": "A03-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "H1. Готовый мини-ПК x86",
        "y": 145.67767333984375,
        "page": 13,
        "old": 3,
        "id": "A03-007",
        "source_ids": [
          "A03-007"
        ],
        "anchor": "A03-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Плюсы: готовое питание, охлаждение, корпус, доступный SSD, привычная Linux-среда. Это короткий путь к автономной станции. Недостатки: нестандартная плата, ограниченный BIOS, сложное обслуживание отдельных узлов и часто непредсказуемая замена Wi-Fi-модуля между партиями.",
        "y": 172.4652099609375,
        "page": 13,
        "old": 3,
        "id": "A03-008",
        "source_ids": [
          "A03-008"
        ],
        "anchor": "A03-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для подбора достаточно начать с класса современного 6–8-ядерного мобильного x86-процессора, 32 ГБ RAM и NVMe, но эти цифры — проектный ориентир, не доказанный минимум. Перед покупкой важнее получить точное имя CPU, PCI ID радио, возможность загрузки Linux, сведения о RAM и настройку восстановления питания. Модель с 16 ГБ тоже может оказаться достаточной после измерений.",
        "y": 240.270751953125,
        "page": 13,
        "old": 3,
        "id": "A03-009",
        "source_ids": [
          "A03-009"
        ],
        "anchor": "A03-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Готовый мини-ПК можно поместить в дизайнерскую оболочку, не разбирая его штатный теплоотвод. Однако «корпус вокруг корпуса» создаёт дополнительные сопротивления воздуху. Сначала проверяется штатная машина, затем оболочка; разница температур показывает цену дизайнерского решения.",
        "y": 323.1142883300781,
        "page": 13,
        "old": 3,
        "id": "A03-010",
        "source_ids": [
          "A03-010"
        ],
        "anchor": "A03-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "H2. Mini-ITX — основа разборного куба",
        "y": 396.5559387207031,
        "page": 13,
        "old": 3,
        "id": "A03-011",
        "source_ids": [
          "A03-011"
        ],
        "anchor": "A03-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Это наиболее прозрачный путь для самостоятельной механики. Плата, процессор, память, SSD, блок питания и вентиляторы выбираются независимо. За эту свободу приходится платить проектированием креплений, кабельных зазоров, радио и питания.",
        "y": 423.3434143066406,
        "page": 13,
        "old": 3,
        "id": "A03-012",
        "source_ids": [
          "A03-012"
        ],
        "anchor": "A03-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Документированный ориентир — Ryzen 7 8700G: 8 ядер, 16 потоков, номинальный TDP 65 Вт, встроенная графика и AM5. Его паспорт не является паспортом всей станции: потребление системы, шум, длительная частота и скорость конкретной speech-модели ещё измеряются. Пример платы — ASRock B650I Lightning WiFi; список поддерживаемых CPU и требуемый BIOS проверяются отдельно от старого PDF-руководства. [1][2][3]",
        "y": 476.1109924316406,
        "page": 13,
        "old": 3,
        "id": "A03-013",
        "source_ids": [
          "A03-013"
        ],
        "anchor": "A03-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первых экспериментов разумны 2 × 16 ГБ DDR5 без разгона и SSD 1 ТБ. Переход на 64 ГБ полезен, если реальные модели или наборы данных не помещаются; покупать память «на всякий случай» вместо проверки голоса не нужно. Встроенная графика не гарантирует ускорение любого ML-runtime. CPU-режим должен оставаться работоспособным базовым путём.",
        "y": 558.95458984375,
        "page": 13,
        "old": 3,
        "id": "A03-014",
        "source_ids": [
          "A03-014"
        ],
        "anchor": "A03-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "H3. Raspberry Pi 5",
        "y": 647.4342651367188,
        "page": 13,
        "old": 3,
        "id": "A03-015",
        "source_ids": [
          "A03-015"
        ],
        "anchor": "A03-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Pi 5 даёт компактную ARM-плату, GPIO и большую экосистему. Она подходит для локального управления, измерений и проверки небольших голосовых моделей. Документация описывает питание 5 В и особенности допустимого тока USB, а также PCIe-подключение периферии; накопитель и охлаждение нужно выбирать как согласованный комплект. [8][9]",
        "y": 674.2217407226562,
        "page": 13,
        "old": 3,
        "id": "A03-016",
        "source_ids": [
          "A03-016"
        ],
        "anchor": "A03-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Недостаток — меньший запас для тяжёлой речи и свободного диалога. Модель, которая комфортно работает на мощном ПК, может оказаться неприемлемой на Pi. Для базы предпочтительнее SSD, а не бесконтрольная запись телеметрии на microSD. Официальные механические файлы полезны для корпуса, но также должны соответствовать ревизии платы. [88]",
        "y": 57.198036193847656,
        "page": 14,
        "old": 3,
        "id": "A03-017",
        "source_ids": [
          "A03-017"
        ],
        "anchor": "A03-017",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "H4. Jetson и другие платы с ускорителем",
        "y": 145.67767333984375,
        "page": 14,
        "old": 3,
        "id": "A03-018",
        "source_ids": [
          "A03-018"
        ],
        "anchor": "A03-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Jetson Orin Nano Super интересен, когда выбранный вычислительный путь действительно использует поддерживаемый NVIDIA стек. Тогда компактная GPU-платформа может быть удобнее дискретной видеокарты в кубе. Ограничения — общий объём памяти, конкретная версия системного образа, совместимость моделей и зависимость от набора драйверов производителя. [10]",
        "y": 172.4652099609375,
        "page": 14,
        "old": 3,
        "id": "A03-019",
        "source_ids": [
          "A03-019"
        ],
        "anchor": "A03-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Другие ARM-платы с NPU сравниваются по тому же правилу: нужен работающий пример нашей модели, а не абстрактный тест. Проверяется преобразование модели, поддержка операций, лицензия, восстановление после ошибки и параллельная работа с аудио. Закрытый драйвер — эксплуатационная зависимость, даже если сам Koobique написан на Rust.",
        "y": 255.3087615966797,
        "page": 14,
        "old": 3,
        "id": "A03-020",
        "source_ids": [
          "A03-020"
        ],
        "anchor": "A03-020",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "H5. Небольшой хаб и отдельный вычислительный worker",
        "y": 343.78839111328125,
        "page": 14,
        "old": 3,
        "id": "A03-021",
        "source_ids": [
          "A03-021"
        ],
        "anchor": "A03-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Локальное управление остаётся на компактной станции, тяжёлая модель исполняется на имеющемся ПК. Это приемлемая лабораторная конфигурация и возможный дополнительный режим. При выключенном ПК пропадает часть голосовых возможностей, но реле, приложение и автоматизации сохраняются. Worker не должен владеть ключом администратора или отправлять произвольные команды устройствам.",
        "y": 370.5758972167969,
        "page": 14,
        "old": 3,
        "id": "A03-022",
        "source_ids": [
          "A03-022"
        ],
        "anchor": "A03-022",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Вариант",
            "Сильная сторона",
            "Основной риск",
            "Проверка до окончательного выбора"
          ],
          [
            "Мини-ПК",
            "Быстрый автономный старт",
            "Скрытая ревизия и драйверы",
            "Linux, Wi-Fi, аудио, перезапуск питания"
          ],
          [
            "Mini-ITX",
            "Ремонт и развитие",
            "Механика, питание, тепло",
            "Габаритный макет и совмещённая нагрузка"
          ],
          [
            "Pi 5",
            "Компактность и документация",
            "Запас speech-производительности",
            "Русские команды в реальном времени"
          ],
          [
            "Jetson",
            "Специализированный вывод",
            "SDK и ограниченная память",
            "Нужная модель на целевом образе"
          ],
          [
            "Хаб + worker",
            "Использование имеющегося ПК",
            "Зависимость расширенного голоса от сети",
            "Отказ worker не влияет на управление"
          ]
        ],
        "y": 456.6,
        "page": 14,
        "old": 3,
        "id": "A03-023",
        "source_ids": [
          "A03-023"
        ],
        "anchor": "A03-023",
        "content_status": "active"
      },
      {
        "kind": "lead",
        "text": "Измерения исследовательских моделей начинаются на уже имеющемся мощном ПК. Состав моделей, которые сможет выполнять куб, определяется по результатам этих измерений.",
        "id": "I22-001",
        "old": "I22",
        "source_ids": [
          "I22-001"
        ],
        "anchor": "I22-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Четыре профиля вычислений",
        "id": "I22-002",
        "old": "I22",
        "source_ids": [
          "I22-002"
        ],
        "anchor": "I22-002",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Профиль",
            "Что выполняется",
            "Решение о покупке"
          ],
          [
            "P0: ядро",
            "Наблюдения, автомат, журнал, небольшие оценщики",
            "Проверить на существующем ПК; отдельный ускоритель не является обязательным"
          ],
          [
            "P1: локальная речь",
            "ASR, намерения, озвучивание",
            "Измерить задержки выбранных моделей и одновременную работу"
          ],
          [
            "P2: малый физический AI",
            "Классификатор, гибридный прогноз, активный сбор",
            "Сравнить CPU и доступный GPU по всей цепочке"
          ],
          [
            "P3: исследование видео/политики",
            "JEPA-подобные модели, обучение, симуляции",
            "Отдельный worker; не превращать его в условие включения света"
          ]
        ],
        "id": "I22-003",
        "old": "I22",
        "source_ids": [
          "I22-003"
        ],
        "anchor": "I22-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Это функциональные профили Koobique, не сертифицированные классы производительности. Конкретные RAM, VRAM и мощность выбираются после фиксированного набора моделей и тестов. Наличие NPU в описании компьютера не подтверждает поддержку нужных операторов.",
        "id": "I22-004",
        "old": "I22",
        "source_ids": [
          "I22-004"
        ],
        "anchor": "I22-004",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Память модели: простой нижний ориентир",
        "id": "I22-005",
        "old": "I22",
        "source_ids": [
          "I22-005"
        ],
        "anchor": "I22-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Если модель имеет KQMATH048TOKEN параметров по KQMATH049TOKEN бит, только веса занимают примерно KQMATH050TOKEN байт. Условные 3 млрд параметров по 4 бита — 1,5 ГБ до метаданных квантования, активаций, кэшей, рабочих буферов и среды исполнения. Нельзя по этой формуле обещать, что вся модель поместится в устройство с 2 ГБ памяти.",
        "id": "I22-006",
        "old": "I22",
        "source_ids": [
          "I22-006"
        ],
        "anchor": "I22-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для потокового звука, нескольких сессий и GPU дополняем бюджет измеренными пиками. Время первого запуска и прогретого выполнения фиксируем отдельно. Диск может быть быстрым, но подгрузка модели всё равно создаёт задержку; частое переключение нескольких крупных моделей сравниваем с постоянной загрузкой одного небольшого сервиса.",
        "id": "I22-007",
        "old": "I22",
        "source_ids": [
          "I22-007"
        ],
        "anchor": "I22-007",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I22",
        "caption": "Вариант первого стенда: станция отвечает за физический контроль, существующий ПК — за тяжёлую модель. Разрыв этой связи уменьшает интеллектуальные функции, но не отнимает локальную кнопку и базовый свет.",
        "id": "I22-008",
        "old": "I22",
        "source_ids": [
          "I22-008"
        ],
        "anchor": "I22-008",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Механика куба остаётся актуальной",
        "id": "I22-009",
        "old": "I22",
        "source_ids": [
          "I22-009"
        ],
        "anchor": "I22-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "AI добавляет к корпусу новые тепловые режимы: простой, постоянную обработку речи, короткий пик и длительное обучение. Их проверяют отдельно на выбранной плате и конструкции, разобранной в главах 5–9. Одна цифра TDP не заменяет испытания собранного куба.",
        "id": "I22-010",
        "old": "I22",
        "edited": true,
        "source_ids": [
          "I22-010"
        ],
        "anchor": "I22-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Микрофон и измеритель температуры выполняют разные задачи. Микрофон может находиться в акустически выбранной части корпуса; комнатный термосенсор не должен измерять тёплый выхлоп. Радиоантенны сохраняют отдельные требования к металлическому корпусу.",
        "id": "I22-011",
        "old": "I22",
        "source_ids": [
          "I22-011"
        ],
        "anchor": "I22-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Увеличение мощности может потребовать другой блок питания, воздушный канал или отдельный вычислительный модуль. Но покупать весь комплект «на будущую универсальную модель» до проверки архитектуры не требуется.",
        "id": "I22-012",
        "old": "I22",
        "source_ids": [
          "I22-012"
        ],
        "anchor": "I22-012",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Небольшой хаб или единый мощный куб",
        "id": "I22-013",
        "old": "I22",
        "source_ids": [
          "I22-013"
        ],
        "anchor": "I22-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Единый компьютер проще в настройке и не зависит от второго устройства, но объединяет тепло, шум и конкуренцию за ресурсы. Хаб плюс worker позволяет исследовать тяжёлые модели на имеющемся ПК, но требует защищённого локального протокола задач и корректной деградации. Несколько станций увеличивают покрытие, но создают проблему единственного владельца исполнительного ресурса.",
        "id": "I22-014",
        "old": "I22",
        "source_ids": [
          "I22-014"
        ],
        "anchor": "I22-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первых экземпляров предлагается сначала хаб/логический core и worker на одном ПК с разными процессами. Затем core переносится в выбранный куб; граница уже проверена. Монтаж устройств и пользовательский сценарий от этого не меняются.",
        "id": "I22-015",
        "old": "I22",
        "source_ids": [
          "I22-015"
        ],
        "anchor": "I22-015",
        "content_status": "active"
      }
    ],
    "origins": [
      "A03",
      "I22"
    ],
    "question": "Нужен ли отдельный куб до первого опыта?",
    "intro": "Сервер, голос и модели можно проверить на имеющемся мощном ПК до покупки станции. Устройства, протокол и данные сохраняются при переносе сервера на отдельный вычислитель.\n\nПри выборе станции вместе со скоростью проверяют загрузку Linux, замену накопителя, драйвер Wi-Fi и восстановление после пропадания питания. Польза ускорителя зависит от поддержки конкретной модели и измеренной нагрузки.",
    "terms": [
      "GPIO — программно управляемый цифровой вывод"
    ],
    "id": "ch-3",
    "slug": "chapter-03"
  },
  {
    "number": 4,
    "title": "Совместимость важнее запаса",
    "group": "Вычислитель",
    "units": [
      {
        "kind": "lead",
        "text": "Работоспособность сборки определяется стыками: сокет, BIOS, память, охлаждение, питание, драйвер и механика.",
        "y": 168.675537109375,
        "page": 15,
        "old": 4,
        "id": "A04-001",
        "source_ids": [
          "A04-001"
        ],
        "anchor": "A04-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "У двух деталей может быть один подходящий разъём и всё же не быть совместимости. CPU помещается в сокет, но не поддерживается текущим BIOS. Wi-Fi-карта физически подходит в M.2, но требует отсутствующего интерфейса. SSD получает питание, но перегревается под соседним радиатором. Поэтому ведомость компонентов должна содержать не только названия, но и основание совместимости.",
        "y": 219.78143310546875,
        "page": 15,
        "old": 4,
        "id": "A04-002",
        "source_ids": [
          "A04-002"
        ],
        "anchor": "A04-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Опорная сборка H2",
        "y": 315.55059814453125,
        "page": 15,
        "old": 4,
        "id": "A04-003",
        "source_ids": [
          "A04-003"
        ],
        "anchor": "A04-003",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Узел",
            "Документированный ориентир",
            "Что допускается заменить",
            "Что проверяется повторно"
          ],
          [
            "CPU",
            "Ryzen 7 8700G",
            "Совместимый AM5 с достаточной CPU-производительностью",
            "BIOS, мощность, высота и крепление кулера"
          ],
          [
            "Плата",
            "B650I Lightning WiFi, Mini-ITX",
            "Другая Mini-ITX с нужными разъёмами",
            "I/O, расположение сокета, радио, fan headers"
          ],
          [
            "RAM",
            "2 × 16 ГБ DDR5, штатный режим",
            "2 × 32 ГБ после потребности",
            "QVL, обучение памяти, стабильность"
          ],
          [
            "SSD",
            "NVMe 1 ТБ",
            "Надёжная модель нужного форм-фактора",
            "Тепло, ресурс, прошивка, восстановление"
          ],
          [
            "CPU-кулер",
            "NH-U9S как ориентир 125 мм",
            "Совместимая башня или низкий кулер",
            "Направление потока, RAM, радиаторы VRM"
          ],
          [
            "Корпусные FAN",
            "3 × 120 мм PWM",
            "NF-A12x25 / P12 или другой проверенный ряд",
            "Толщина, ток, кривая давления, разъём"
          ],
          [
            "БП",
            "Закрытый SFX с родными кабелями",
            "Другая сертифицированная модель достаточной мощности",
            "Размеры, разъёмы, кабельный объём"
          ]
        ],
        "y": 344.67,
        "page": 15,
        "old": 4,
        "id": "A04-004",
        "source_ids": [
          "A04-004"
        ],
        "anchor": "A04-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Паспорт NH-U9S и руководства платы нужны именно для геометрической проверки, а не для обещания универсальной совместимости. У рассмотренной платы руководство указывает допустимую нагрузку до 2 А / 24 Вт для перечисленных CPU/WP и chassis/WP headers; переносить это число на другую плату нельзя. [3][5]",
        "y": 57.42815399169922,
        "page": 16,
        "old": 4,
        "id": "A04-005",
        "source_ids": [
          "A04-005"
        ],
        "anchor": "A04-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "У SF750 большой запас мощности относительно планируемой конфигурации без дискретной GPU. Это пример документированного SFX-формата, а не требование покупать 750 Вт. Выбор меньшего БП определяется реальной конфигурацией, качеством и доступностью разъёмов; экономия на номинале не должна приводить к сомнительному источнику питания. [7]",
        "y": 129.83209228515625,
        "page": 16,
        "old": 4,
        "id": "A04-006",
        "source_ids": [
          "A04-006"
        ],
        "anchor": "A04-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Как распределить память",
        "y": 225.6012725830078,
        "page": 16,
        "old": 4,
        "id": "A04-007",
        "source_ids": [
          "A04-007"
        ],
        "anchor": "A04-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Вместо требования «ядро обязано занимать 64 МБ» задаём отдельные бюджеты. Rust-core имеет ограниченные очереди и контролируемый кэш. База ограничивает объём выборок. Speech-worker получает собственный предел памяти. Ускоритель, если он использует общую RAM, учитывается как конкурент остальных процессов, а не как бесплатный ресурс.",
        "y": 252.618896484375,
        "page": 16,
        "old": 4,
        "id": "A04-008",
        "source_ids": [
          "A04-008"
        ],
        "anchor": "A04-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Пример планирования для 32 ГБ: ОС и службы получают несколько гигабайт резерва; собственное ядро и БД — отдельный измеряемый бюджет; основная часть остаётся моделям и файловому кэшу. Это не статическое разбиение памяти и не гарантия конкретной модели. Политика должна выдерживать пиковое переподключение узлов одновременно с запуском STT и резервным копированием.",
        "y": 340.9978332519531,
        "page": 16,
        "old": 4,
        "id": "A04-009",
        "source_ids": [
          "A04-009"
        ],
        "anchor": "A04-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Вытеснение голосового worker лучше, чем вытеснение единственного процесса управления. Но корректнее не доводить систему до OOM: ограничить число одновременных inference-запросов, отклонять лишние с понятным ответом и заранее загрузить часто используемую небольшую модель.",
        "y": 429.37677001953125,
        "page": 16,
        "old": 4,
        "id": "A04-010",
        "source_ids": [
          "A04-010"
        ],
        "anchor": "A04-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "CPU, GPU и NPU: какой тест нужен",
        "y": 509.1709289550781,
        "page": 16,
        "old": 4,
        "id": "A04-011",
        "source_ids": [
          "A04-011"
        ],
        "anchor": "A04-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для каждого backend сохраняются имя модели, хеш весов, формат квантования, runtime, число потоков, входное аудио, время первого ответа, длительность полного распознавания и пиковая память. Проверяется русский и английский корпус команд, а не только английская демонстрационная фраза.",
        "y": 536.1885375976562,
        "page": 16,
        "old": 4,
        "id": "A04-012",
        "source_ids": [
          "A04-012"
        ],
        "anchor": "A04-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Современные проекты вывода предлагают разные ускорители. Например, whisper.cpp документирует несколько CPU/GPU/NPU-путей, но перечень в README не доказывает, что конкретная плата и версия драйвера подходят нашему профилю. Именно поэтому аппаратный выбор привязан к воспроизводимому прогону. [61]",
        "y": 608.592529296875,
        "page": 16,
        "old": 4,
        "id": "A04-013",
        "source_ids": [
          "A04-013"
        ],
        "anchor": "A04-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Проверки при получении комплектующих",
        "y": 56.314456939697266,
        "page": 17,
        "old": 4,
        "id": "A04-014",
        "source_ids": [
          "A04-014"
        ],
        "anchor": "A04-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Плата фотографируется с маркировкой ревизии и разъёмов. Записываются версия UEFI, серийный номер SSD и идентификаторы радио. Выполняется загрузка Linux с последующей проверкой сети, USB-аудио, температуры и восстановления после обычного выключения. Затем запускаются нагрузка CPU и диска, несколько последовательных перезапусков и копирование большого файла с проверкой хеша.",
        "y": 83.33208465576172,
        "page": 17,
        "old": 4,
        "id": "A04-015",
        "source_ids": [
          "A04-015"
        ],
        "anchor": "A04-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Не следует начинать с разгона, undervolt или отключения защит ядра ради красивой задержки. Сначала нужен воспроизводимый штатный режим. Изменение лимитов питания выполняется по одному параметру и сопровождается повторным тепловым и функциональным испытанием. Аппаратные уязвимости и их mitigations являются отдельным фактором безопасности; их нельзя молча выключать в «оптимизированном образе». [87]",
        "y": 171.71102905273438,
        "page": 17,
        "old": 4,
        "id": "A04-016",
        "source_ids": [
          "A04-016"
        ],
        "anchor": "A04-016",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Карточка совместимости",
        "y": 292.3348693847656,
        "page": 17,
        "old": 4,
        "id": "A04-017",
        "source_ids": [
          "A04-017"
        ],
        "anchor": "A04-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для каждой пары «плата—CPU», «плата—RAM», «плата—Wi-Fi», «CPU—кулер», «плата— корпус» сохранить ссылку, ревизию, проверенный факт и результат стенда. Если ячейка основана только на предположении продавца, статус остаётся «проверить», даже когда остальные детали уже куплены.",
        "y": 310.7960510253906,
        "page": 17,
        "old": 4,
        "id": "A04-018",
        "source_ids": [
          "A04-018"
        ],
        "anchor": "A04-018",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Дополнительные устройства хранения и питания",
        "y": 398.0737609863281,
        "page": 17,
        "old": 4,
        "id": "A04-019",
        "source_ids": [
          "A04-019"
        ],
        "anchor": "A04-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Имеющийся внешний SSD полезен для резервных копий, аудиокорпуса и переноса образов. Для постоянной системной БД внутренний накопитель уменьшает риск случайного отсоединения. USB-адаптеры NVMe отличаются контроллерами, охлаждением и поведением при сбое — внешний диск не становится ненадёжным по определению, но требует собственного теста.",
        "y": 425.09136962890625,
        "page": 17,
        "old": 4,
        "id": "A04-020",
        "source_ids": [
          "A04-020"
        ],
        "anchor": "A04-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "UPS имеет смысл после первого устойчивого стенда. Он должен предоставлять станции сигнал о разряде и достаточное время для завершения критических операций. Бесконечно держать питание реле при потере управляющего сервера не является универсально безопасным поведением: режим нагрузки задаётся отдельно.",
        "y": 513.4703369140625,
        "page": 17,
        "old": 4,
        "id": "A04-021",
        "source_ids": [
          "A04-021"
        ],
        "anchor": "A04-021",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Ресурсный бюджет сервера",
        "y": 225.6012725830078,
        "page": 137,
        "old": 40,
        "id": "A40-026",
        "source_ids": [
          "A40-026"
        ],
        "anchor": "A40-026",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Бюджет станции состоит из рабочего набора Core, БД, сетевых буферов, ОС, модели речи и временных пиков. Для голоса полезно сравнивать не только размер файла весов, но и максимальную RAM/VRAM, время загрузки, real-time factor и задержку первой пригодной гипотезы.",
        "y": 252.618896484375,
        "page": 137,
        "old": 40,
        "id": "A40-027",
        "source_ids": [
          "A40-027"
        ],
        "anchor": "A40-027",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Если десятисекундный фрагмент обрабатывается за четыре секунды, RTF = 0,4. Это не означает ответ через 0,4 секунды: добавляются окончание фразы, очередь, разрешение намерения и исполнение. Если система одновременно записывает историю и обновляет модель, повторяют тот же тест под этой нагрузкой.",
        "y": 325.0228576660156,
        "page": 137,
        "old": 40,
        "id": "A40-028",
        "source_ids": [
          "A40-028"
        ],
        "anchor": "A40-028",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Рабочий набор собственного ядра измеряется отдельно от файлового кэша Linux. Результат хранит версии BIOS, ядра, драйвера, runtime, модели и способ квантования. Без этого сравнение Ryzen, Pi и Jetson не воспроизводится.",
        "y": 397.4267578125,
        "page": 137,
        "old": 40,
        "id": "A40-029",
        "source_ids": [
          "A40-029"
        ],
        "anchor": "A40-029",
        "content_status": "active"
      },
      {
        "id": "N-001",
        "kind": "h2",
        "text": "Файл модели, рабочая память и задержка — разные величины",
        "new": true,
        "source_ids": [
          "N-001"
        ],
        "anchor": "N-001",
        "content_status": "active"
      },
      {
        "id": "N-002",
        "kind": "p",
        "text": "Для каждой выбранной модели составляем одну измерительную карточку. В неё входят точный файл и его хеш, формат весов, способ квантования, runtime, устройство исполнения, число потоков, память в покое и на пике, время загрузки и задержка на одном и том же наборе входов. Сравнение проводится вместе с работающим ядром, журналом и аудио, а не на свободном компьютере.",
        "new": true,
        "source_ids": [
          "N-002"
        ],
        "anchor": "N-002",
        "content_status": "active"
      },
      {
        "id": "R04-001",
        "kind": "h2",
        "text": "Четыре вентилятора и три разъёма",
        "source_ids": [
          "R04-001"
        ],
        "anchor": "R04-001",
        "content_status": "active"
      },
      {
        "id": "R04-002",
        "kind": "p",
        "text": "В руководстве B650I Lightning WiFi указаны два CPU/WP и один CHA/WP — всего три разъёма. Три корпусных вентилятора плюс кулер нельзя считать четырьмя независимо наблюдаемыми каналами. Разветвитель обычно возвращает только один тахосигнал. Варианты: уменьшить число вентиляторов, принять групповое управление с явно неполной диагностикой или поставить документированный контроллер с отдельными таховходами. Проверяются ток каждого порта и суммарный пусковой ток. [3]",
        "source_ids": [
          "R04-002"
        ],
        "anchor": "R04-002",
        "content_status": "active"
      },
      {
        "id": "R04-003",
        "kind": "p",
        "text": "Модель Wi-Fi-чипа определяем по PCI/USB ID и реально загруженному драйверу, а не по ссылке на Intel в книге. Для встроенной графики также проверяется конкретный backend модели; наличие Radeon не является обещанием поддержки ROCm. Перед выбором H2 нужно сравнить CPU, Vulkan и доступные официально поддерживаемые ускорители на одном наборе задач.",
        "source_ids": [
          "R04-003"
        ],
        "anchor": "R04-003",
        "content_status": "active"
      },
      {
        "id": "N-003",
        "kind": "p",
        "text": "Практический результат должен звучать как проверенное условие: «эта конфигурация обрабатывает такой набор команд при такой нагрузке». Из размера файла или рекламного числа операций в секунду такой вывод не следует. Поддержка ускорителя в runtime также не означает, что все операции выбранной модели выполняются на нём; это проверяется профилем исполнения. [144]",
        "new": true,
        "source_ids": [
          "N-003"
        ],
        "anchor": "N-003",
        "content_status": "active"
      }
    ],
    "origins": [
      "A04",
      [
        "A40",
        26,
        29
      ]
    ],
    "question": "Почему подходящие по отдельности детали могут не сложиться в систему?",
    "intro": "Разъём — только начало совместимости. Процессор может подходить к сокету, но требовать более нового BIOS. Плата может иметь Wi-Fi, но неудобный для выбранного Linux драйвер. Модель может помещаться на диске, но не в оперативной памяти во время работы.\n\nПоэтому до заказа составляем короткую цепочку доказательств: точная ревизия детали, документированная поддержка, небольшой проверочный запуск и измерение под общей нагрузкой. Красивая таблица характеристик не заменяет эту цепочку.",
    "id": "ch-4",
    "slug": "chapter-04"
  },
  {
    "number": 5,
    "title": "Куб: от объёма к деталям",
    "group": "Конструкция",
    "units": [
      {
        "kind": "lead",
        "text": "Сначала определяются объёмы деталей, воздушные каналы и обслуживание. Оболочка подчиняется этой геометрии.",
        "y": 168.675537109375,
        "page": 18,
        "old": 5,
        "id": "A05-001",
        "source_ids": [
          "A05-001"
        ],
        "anchor": "A05-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для самостоятельной сборки удобнее разборная рама, чем цельный сложный корпус. У неё понятные базовые плоскости, заменяемые панели и возможность исправить одну деталь, не печатая всё заново. Опорный вариант Koobique — куб на профиле 2020 с металлическим поддоном платы, съёмной передней фильтрующей кассетой и радиопрозрачной зоной для антенн.",
        "y": 219.78143310546875,
        "page": 18,
        "old": 5,
        "id": "A05-002",
        "source_ids": [
          "A05-002"
        ],
        "anchor": "A05-002",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A04",
        "caption": "Принцип разборного куба: несущая рама, отдельный поддон, нижний отсек питания и съёмные панели. Изображение показывает идею сборки, а не финальную геометрию конкретной платы.",
        "y": 315.9290466308594,
        "page": 18,
        "old": 5,
        "id": "A05-003",
        "source_ids": [
          "A05-003"
        ],
        "anchor": "A05-003",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Параметрическая система координат",
        "y": 56.314456939697266,
        "page": 19,
        "old": 5,
        "id": "A05-004",
        "source_ids": [
          "A05-004"
        ],
        "anchor": "A05-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Примем наружную грань рамы 320 мм, профиль 20 × 20 мм. Внутренний чистый размер между профилями — 280 мм. Ось X направлена вправо, Y — от передней панели к задней, Z — вверх. Начало координат находится на наружном переднем левом нижнем углу рамы. Внешние панели толщиной 2 мм увеличат габарит примерно до 324 мм; ножки и антенны учитываются отдельно.",
        "y": 83.33208465576172,
        "page": 19,
        "old": 5,
        "id": "A05-005",
        "source_ids": [
          "A05-005"
        ],
        "anchor": "A05-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для конкретной схемы стыковки можно использовать четыре поперечины длиной 320 мм и восемь отрезков 280 мм. Полноразмерные элементы идут вдоль X на передней и задней гранях верхнего и нижнего квадратов; боковые горизонтальные и четыре стойки вставляются между ними. Эта ведомость не подходит автоматически для других уголков, скрытых соединителей или профиля с иной геометрией.",
        "y": 171.71102905273438,
        "page": 19,
        "old": 5,
        "id": "A05-006",
        "source_ids": [
          "A05-006"
        ],
        "anchor": "A05-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Размерная цепь должна содержать не только номинал, но и допуски резки, заусенец, толщину покрытия и зазор сборки. Прямоугольность рамы проверяется диагоналями до установки панелей. Панель не должна насильно выправлять перекошенный каркас: это создаёт остаточные напряжения, щели фильтра и шум.",
        "y": 260.0899658203125,
        "page": 19,
        "old": 5,
        "id": "A05-007",
        "source_ids": [
          "A05-007"
        ],
        "anchor": "A05-007",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Реальная укладка Mini-ITX",
        "y": 339.8841552734375,
        "page": 19,
        "old": 5,
        "id": "A05-008",
        "source_ids": [
          "A05-008"
        ],
        "anchor": "A05-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Плата Mini-ITX имеет собственную монтажную систему координат. Её задние I/O сначала совмещают с фактической плоскостью I/O shield; от неё откладывают глубину 170 мм. Прежний условный прямоугольник Y = 130…300 нельзя использовать как монтажную базу при задней панели Y = 320: он создаёт около 20 мм колодца. X, Z, стойки и вырез утверждаются только по конкретной плате и штекерам.",
        "y": 366.9017639160156,
        "page": 19,
        "old": 5,
        "id": "A05-009",
        "source_ids": [
          "A05-009"
        ],
        "anchor": "A05-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Процессорный кулер высотой 125 мм над верхней плоскостью CPU оставляет запас до крыши только после учёта высоты сокета и платы. Его координаты определяются фактическим сокетом, а не центром материнской платы. Область вокруг RAM, VRM и крепёжной рамки проверяется в 3D и затем физически. Файлы размеров производителей полезнее усреднённого «кубика процессора». [3][5]",
        "y": 439.30572509765625,
        "page": 19,
        "old": 5,
        "id": "A05-010",
        "source_ids": [
          "A05-010"
        ],
        "anchor": "A05-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "SFX можно исследовать в нижнем отсеке как объём порядка 125 × 100 × 63,5 мм, но его вентилятор должен иметь отдельный незагороженный вход по чертежу модели. Положение Z = 30…94 при поддоне Z ≈ 105 оставляет лишь около 11 мм над БП и не утверждается как пригодное. Предпочтительный макет: собственный вход БП снизу/сбоку и независимый выход назад; ориентация и зазор подтверждаются до резки. Перед модульными разъёмами резервируется отдельный объём для штекеров, выхода и допустимого изгиба родных кабелей. Габарит корпуса SFX без этого объёма не задаёт монтажную оболочку; её проверяют по выбранной модели и макету жгута.",
        "y": 527.6846313476562,
        "page": 19,
        "old": 5,
        "id": "A05-011",
        "source_ids": [
          "A05-011"
        ],
        "anchor": "A05-011",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A05",
        "caption": "Проверка геометрии куба: I/O базируется от задней панели, поддон и приток согласуются до изготовления. Размеры — компоновочные, не утверждённый рабочий чертёж.",
        "y": 56.69291687011719,
        "page": 20,
        "old": 5,
        "id": "A05-012",
        "source_ids": [
          "A05-012"
        ],
        "anchor": "A05-012",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Расположение вентиляторов",
        "y": 452.56768798828125,
        "page": 20,
        "old": 5,
        "id": "A05-013",
        "source_ids": [
          "A05-013"
        ],
        "anchor": "A05-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Два 120-мм вентилятора с центрами X = 160, Z = 90 и 230 геометрически помещаются на раме, но нижний проём Z = 30…150 пересекает поддон около Z = 105. Эти координаты сохраняются как эскиз фасада, не готовый воздушный тракт. В рабочем макете поддон получает проверенный канал/вырез либо нижний вентилятор переносится. Доли потока по одной площади рассчитать нельзя: нужны сопротивления обеих ветвей и измерение. [4]",
        "y": 479.5853271484375,
        "page": 20,
        "old": 5,
        "id": "A05-014",
        "source_ids": [
          "A05-014"
        ],
        "anchor": "A05-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Задний вентилятор предлагается поставить в верхней зоне, например с центром X = 160, Z = 235 мм. Его нижняя кромка не должна перекрывать I/O shield. Это проверяется по фактической высоте платы и выступам штекеров. Если места нет, не следует сдвигать плату вслепую: возможны верхняя вытяжка, боковой 120/140-мм вентилятор или увеличение корпуса.",
        "y": 583.9392700195312,
        "page": 20,
        "old": 5,
        "id": "A05-015",
        "source_ids": [
          "A05-015"
        ],
        "anchor": "A05-015",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Доступ, который стоит заложить сразу",
        "y": 679.7084350585938,
        "page": 20,
        "old": 5,
        "id": "A05-016",
        "source_ids": [
          "A05-016"
        ],
        "anchor": "A05-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Фильтр извлекается снаружи без снятия материнской платы. Кнопка питания и отдельная кнопка сопряжения различаются на ощупь. Сервисный USB и индикатор диагностики доступны сзади либо под небольшим лючком. Крепёж панели имеет удержание или хранится в подписи на детали; выпадение винта на работающую плату недопустимо.",
        "y": 706.72607421875,
        "page": 20,
        "old": 5,
        "id": "A05-017",
        "source_ids": [
          "A05-017"
        ],
        "anchor": "A05-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Внутри нужны точки крепления жгутов, запас для вынимания SSD и свободный доступ к защёлкам RAM. Накопитель не должен оказаться под несъёмным воздуховодом. Провод антенны не прокладывается через острое отверстие без защиты края.",
        "y": 57.42815399169922,
        "page": 21,
        "old": 5,
        "id": "A05-018",
        "source_ids": [
          "A05-018"
        ],
        "anchor": "A05-018",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Альтернативы корпуса",
        "y": 121.24732971191406,
        "page": 21,
        "old": 5,
        "id": "A05-019",
        "source_ids": [
          "A05-019"
        ],
        "anchor": "A05-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Компактная оболочка для мини-ПК проще: собственная рама несёт готовую машину, USB-модуль и фильтр. Штатный кулер не переделывается, но добавленная оболочка проходит отдельный тепловой тест.",
        "y": 148.26495361328125,
        "page": 21,
        "old": 5,
        "id": "A05-020",
        "source_ids": [
          "A05-020"
        ],
        "anchor": "A05-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Цельный печатный корпус оправдан для Pi или небольшого хаба. Большой куб выгоднее разбить на угловые стойки, панели и кассеты. Это снижает риск деформации длинной печати и позволяет заменить одну функциональную часть.",
        "y": 204.69390869140625,
        "page": 21,
        "old": 5,
        "id": "A05-021",
        "source_ids": [
          "A05-021"
        ],
        "anchor": "A05-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Куб с GPU — другая компоновка. Длину видеокарты, питание и радиус кабелей нельзя считать уже учтёнными в 320-мм варианте. Для неё создаётся отдельная конфигурация CAD, например с увеличенной гранью, а не незаметное расширение ведомости того же корпуса.",
        "y": 261.1228332519531,
        "page": 21,
        "old": 5,
        "id": "A05-022",
        "source_ids": [
          "A05-022"
        ],
        "anchor": "A05-022",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "До заказа металла",
        "y": 349.7966613769531,
        "page": 21,
        "old": 5,
        "id": "A05-023",
        "source_ids": [
          "A05-023"
        ],
        "anchor": "A05-023",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Из картона или дешёвой печати собрать полноразмерные объёмы платы, БП, кулера и вентиляторов. Добавить реальные кабели. Проверить вынимание фильтра, SSD и платы. Только после этого выпускать панели в металл. Такое испытание быстрее исправляет компоновку, чем окончательная фрезеровка.",
        "y": 368.25787353515625,
        "page": 21,
        "old": 5,
        "id": "A05-024",
        "source_ids": [
          "A05-024"
        ],
        "anchor": "A05-024",
        "content_status": "active"
      },
      {
        "kind": "lead",
        "text": "Приведённые параметры позволяют подготовить чертежи и испытания корпуса. Перед изготовлением необходимо сверить реальные детали и посадки; исходных размеров недостаточно для отправки всего корпуса в резку.",
        "y": 168.675537109375,
        "page": 134,
        "old": 40,
        "id": "A40-001",
        "source_ids": [
          "A40-001"
        ],
        "anchor": "A40-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для рамы 320 мм и наружной панели 2 мм используется учебный контур 324 × 324 мм. Координаты этой панели локальные: начало в её нижнем левом углу. Центры двух вентиляторов относительно панели — (162; 92) и (162; 232) мм. Смещение на 2 мм относительно рамы учитывает наружный размер панели.",
        "y": 237.18142700195312,
        "page": 134,
        "old": 40,
        "id": "A40-002",
        "source_ids": [
          "A40-002"
        ],
        "anchor": "A40-002",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A42",
        "caption": "Параметрическая передняя панель. Разметка относится к отдельной панели, а не к координатам платы. Все размеры в миллиметрах; окончательный контур выпускается после примерки рамы, вентилятора и кассеты.",
        "y": 317.35406494140625,
        "page": 134,
        "old": 40,
        "id": "A40-003",
        "source_ids": [
          "A40-003"
        ],
        "anchor": "A40-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для межосевого квадрата 105 мм получаются восемь отверстий вентилятора: X = 109,5 и 214,5 мм; нижние Z = 39,5 и 144,5 мм; верхние Z = 179,5 и 284,5 мм. Диаметр 4,5 мм показан только как пример проходного отверстия под выбранную схему M4. Самонарезающий винт вентилятора, резиновое крепление и резьбовая вставка требуют другой детали соединения. [4]",
        "y": 57.42815399169922,
        "page": 135,
        "old": 40,
        "id": "A40-004",
        "source_ids": [
          "A40-004"
        ],
        "anchor": "A40-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Круглые проёмы диаметром 116 мм — исходная геометрия для примерки. Они не являются обязательным паспортным размером любого 120-мм вентилятора. Проверяются сечение входа, доступ к винтам, решётка, виброразвязка и отсутствие контакта лопасти при деформации. Защитная решётка и фильтр остаются отдельными элементами, их нельзя забыть при оценке сопротивления.",
        "y": 145.80709838867188,
        "page": 135,
        "old": 40,
        "id": "A40-005",
        "source_ids": [
          "A40-005"
        ],
        "anchor": "A40-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Крепления к раме можно расположить по центрам лицевых пазов, около 12 и 312 мм от края панели в принятой системе. Реальная геометрия T-гаек, уголков и торцевых соединений определяет окончательные отверстия. Панель предварительно накладывается на собранный каркас и проверяется без усилия.",
        "y": 234.18603515625,
        "page": 135,
        "old": 40,
        "id": "A40-006",
        "source_ids": [
          "A40-006"
        ],
        "anchor": "A40-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Допуски и сборочные зазоры",
        "y": 313.9801940917969,
        "page": 135,
        "old": 40,
        "id": "A40-007",
        "source_ids": [
          "A40-007"
        ],
        "anchor": "A40-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Предположим, рама по одному размеру получилась 320,0 ±0,3 мм, а паз кассеты напечатан с отклонением ±0,4 мм. Если назначить нулевой номинальный зазор, худшее сочетание даёт натяг до 0,7 мм. Это арифметический пример, не гарантированная точность конкретного производства.",
        "y": 340.9978332519531,
        "page": 135,
        "old": 40,
        "id": "A40-008",
        "source_ids": [
          "A40-008"
        ],
        "anchor": "A40-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для скользящей кассеты сначала печатают короткую пробу с несколькими зазорами и проверяют её после охлаждения и отделки. Выбранный зазор зависит от длины направляющей, материала, ориентации слоёв, покрытия и ожидаемой температуры. Универсальная фраза «добавить 0,2 мм» не заменяет такой купон.",
        "y": 413.40179443359375,
        "page": 135,
        "old": 40,
        "id": "A40-009",
        "source_ids": [
          "A40-009"
        ],
        "anchor": "A40-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Один размер не должен одновременно базировать деталь по всем осям через несколько жёстких креплений. Дополнительные крепления могут иметь продолговатые отверстия, позволяющие компенсацию и не требующие изгиба панели. Базы обозначаются на чертеже: откуда отсчитывается отверстие и какая грань действительно задаёт положение.",
        "y": 485.80572509765625,
        "page": 135,
        "old": 40,
        "id": "A40-010",
        "source_ids": [
          "A40-010"
        ],
        "anchor": "A40-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для гибки металлической панели учитываются радиус, толщина и нейтральный слой. Развёртку получают из подтверждённых параметров исполнителя или пробного гиба. Отдельная плоская панель на профиле проще для первого куба именно потому, что убирает эту неопределённость. [13]",
        "y": 574.1846313476562,
        "page": 135,
        "old": 40,
        "id": "A40-011",
        "source_ids": [
          "A40-011"
        ],
        "anchor": "A40-011",
        "content_status": "active"
      },
      {
        "id": "R05-001",
        "kind": "h2",
        "text": "Компоновочный выпуск после примерки",
        "source_ids": [
          "R05-001"
        ],
        "anchor": "R05-001",
        "content_status": "active"
      },
      {
        "id": "R05-002",
        "kind": "p",
        "text": "Сохранённый DXF фасада — шаблон рамы и вентиляторов, не разрешение изготавливать весь корпус. В рабочий выпуск входят четыре независимых подтверждения: доступ штекеров к I/O; сечение воздушных каналов после установки поддона; вход и выхлоп БП; траектория извлечения фильтра и SSD. Ни одно из них нельзя заменить красивым общим видом.",
        "source_ids": [
          "R05-002"
        ],
        "anchor": "R05-002",
        "content_status": "active"
      },
      {
        "id": "R05-003",
        "kind": "table",
        "rows": [
          [
            "Коллизия макета",
            "Как исправить",
            "Что проверить руками"
          ],
          [
            "I/O утоплен",
            "Базировать плату от заднего щита",
            "Все нужные штекеры входят и вынимаются"
          ],
          [
            "Поддон пересекает приток",
            "Канал, перфорация либо перенос вентилятора",
            "Нет нагретого тупикового отсека"
          ],
          [
            "Вход БП под поддоном",
            "Отдельный нижний/боковой тракт",
            "Решётка и фильтр не упираются в вентилятор"
          ],
          [
            "Фильтр мешает кабелю",
            "Изменить кассету или крепление жгута",
            "Пять циклов обслуживания без касания платы"
          ]
        ],
        "source_ids": [
          "R05-003"
        ],
        "anchor": "R05-003",
        "content_status": "active"
      }
    ],
    "origins": [
      "A05",
      [
        "A40",
        1,
        11
      ]
    ],
    "question": "Как превратить красивый куб в обслуживаемую конструкцию?",
    "intro": "Сначала в корпусе размещают плату, кулер, блок питания, вентиляторы, штекеры и изгибы проводов. Затем проверяют, как достать кабель, очистить фильтр и заменить накопитель: для рук и инструмента тоже нужен объём. После этого определяют наружные панели.\n\nУ рамы и отдельной наружной панели разные начала координат. Поэтому центр одного вентилятора может иметь разные координаты на общем виде и на чертеже панели. Обе базы должны быть явно обозначены.",
    "id": "ch-5",
    "slug": "chapter-05"
  },
  {
    "number": 6,
    "title": "Металл, пластик и крепления",
    "group": "Конструкция",
    "units": [
      {
        "kind": "lead",
        "text": "Условия работы каждой детали определяют выбор материала. Внешний вид образца дополняет проверку температуры, нагрузки и способа изготовления.",
        "y": 168.675537109375,
        "page": 22,
        "old": 6,
        "id": "A06-001",
        "source_ids": [
          "A06-001"
        ],
        "anchor": "A06-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Металл, FDM и фотополимерная печать решают разные задачи. Жёсткий поддон платы удобно делать металлическим; фильтрующую кассету, воздуховод, кнопку и радиопрозрачную крышку — печатными. Сочетание материалов обычно практичнее попытки изготовить всё одним способом.",
        "y": 219.5513153076172,
        "page": 22,
        "old": 6,
        "id": "A06-002",
        "source_ids": [
          "A06-002"
        ],
        "anchor": "A06-002",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A06",
        "caption": "Поперечное сечение фильтрующей зоны. Кассета, уплотнение и вентилятор — отдельные детали. Схема показывает порядок слоёв, не их окончательную толщину.",
        "y": 296.2060546875,
        "page": 22,
        "old": 6,
        "id": "A06-003",
        "source_ids": [
          "A06-003"
        ],
        "anchor": "A06-003",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Металлический вариант",
        "y": 644.0021362304688,
        "page": 22,
        "old": 6,
        "id": "A06-004",
        "source_ids": [
          "A06-004"
        ],
        "anchor": "A06-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Алюминиевые панели порядка 1,5–2 мм являются стартовым ориентиром для небольшого разборного корпуса, но жёсткость зависит от размера пролёта, перфорации и отбортовок. Тонкая плоская стенка с крупными отверстиями может звенеть. Загиб края или отдельная рамка часто эффективнее простого увеличения толщины. Стальной лист позволяет уменьшить толщину при иной массе и технологии обработки; он также требует защиты кромок и покрытия.",
        "y": 670.7896118164062,
        "page": 22,
        "old": 6,
        "id": "A06-005",
        "source_ids": [
          "A06-005"
        ],
        "anchor": "A06-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Лазерная резка удобна для панелей и пазов. Гибка требует учёта радиуса инструмента, направления и развёртки. Фрезеровка подходит для точных посадок, но не обязательна для всех стенок. Перфорация вокруг вентилятора должна оставлять достаточную открытую площадь и не создавать узкую решётку вплотную к лопастям.",
        "y": 57.198036193847656,
        "page": 23,
        "old": 6,
        "id": "A06-006",
        "source_ids": [
          "A06-006"
        ],
        "anchor": "A06-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Отверстия под панель можно выполнять с небольшим монтажным зазором или в виде коротких пазов, если это не портит базирование. Один участок определяет положение, остальные позволяют собрать деталь без натяга. Винтовое соединение с покрытием не считается автоматически надёжным электрическим контактом защитного проводника. Электрическое соединение металлических частей проектируется отдельно от механической стяжки.",
        "y": 125.00357818603516,
        "page": 23,
        "old": 6,
        "id": "A06-007",
        "source_ids": [
          "A06-007"
        ],
        "anchor": "A06-007",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "FDM: PETG, ASA и более требовательные материалы",
        "y": 228.521240234375,
        "page": 23,
        "old": 6,
        "id": "A06-008",
        "source_ids": [
          "A06-008"
        ],
        "anchor": "A06-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "PETG удобен для кассет, кабельных держателей и первых панелей. ASA интересен для деталей, где нужны большая температурная устойчивость и устойчивость к внешней среде, но сложнее в печати. Руководства Prusa отдельно описывают режимы и ограничения обоих материалов; конкретная марка и ориентация печати остаются важными. [11][12][89]",
        "y": 255.3087615966797,
        "page": 23,
        "old": 6,
        "id": "A06-009",
        "source_ids": [
          "A06-009"
        ],
        "anchor": "A06-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Не следует превращать температуру размягчения из рекламной карточки в разрешённую температуру длительной работы нагруженного крепления. Ползучесть под винтом, близость радиатора, ориентация слоёв и остаточные напряжения могут разрушить деталь раньше. Для нагруженных мест полезны металлические вставки, широкая опорная площадь и конструкция, в которой слой не разрывается основным усилием.",
        "y": 323.1142883300781,
        "page": 23,
        "old": 6,
        "id": "A06-010",
        "source_ids": [
          "A06-010"
        ],
        "anchor": "A06-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Большую панель лучше разделить по функциональным границам. Воздуховод можно распечатать отдельно от крепления платы, чтобы его изменение не требовало переделки корпуса. Стыки снабжаются небольшими направляющими и прокладкой, но не должны мешать обслуживанию.",
        "y": 405.9578552246094,
        "page": 23,
        "old": 6,
        "id": "A06-011",
        "source_ids": [
          "A06-011"
        ],
        "anchor": "A06-011",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Фотополимерная печать",
        "y": 479.3994445800781,
        "page": 23,
        "old": 6,
        "id": "A06-012",
        "source_ids": [
          "A06-012"
        ],
        "anchor": "A06-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "SLA/MSLA подходит для точной кнопки, микрофонного держателя, оптической рамки и небольших сложных деталей. Для большой силовой рамы она не является автоматически лучшим выбором: важны ударная прочность, старение, постотверждение и стоимость. Высокотемпературная смола может иметь впечатляющий HDT после заданного цикла обработки, но это не универсальная длительная рабочая температура изделия. [77]",
        "y": 506.1869812011719,
        "page": 23,
        "old": 6,
        "id": "A06-013",
        "source_ids": [
          "A06-013"
        ],
        "anchor": "A06-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Неполностью отверждённые детали не устанавливают рядом с датчиком влажности или в аудиоканал. Порядок промывки и отверждения выполняется по паспорту смолы. Сенсорные и оптические отверстия не заполняются лаком ради косметики.",
        "y": 589.0305786132812,
        "page": 23,
        "old": 6,
        "id": "A06-014",
        "source_ids": [
          "A06-014"
        ],
        "anchor": "A06-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Крепления и вибрации",
        "y": 647.4342651367188,
        "page": 23,
        "old": 6,
        "id": "A06-015",
        "source_ids": [
          "A06-015"
        ],
        "anchor": "A06-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для панели удобен метрический крепёж с резьбовыми вставками или гайками, которые можно заменить. Саморез в пластик допускается в небольшом числе обслуживаний, но это должно быть сознательное решение. Термовставка нуждается в достаточной толщине стенки; универсальное «отверстие под M3» без модели вставки не является чертежом.",
        "y": 674.2217407226562,
        "page": 23,
        "old": 6,
        "id": "A06-016",
        "source_ids": [
          "A06-016"
        ],
        "anchor": "A06-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Вентилятор устанавливается так, чтобы крепление не сжимало рамку до деформации. Эластичная прокладка уменьшает передачу части вибраций, но чрезмерно мягкое подвешивание может увеличить амплитуду колебаний и ухудшить зазор. Поддон и тяжёлый кулер должны сохранять геометрию при переносе корпуса.",
        "y": 57.198036193847656,
        "page": 24,
        "old": 6,
        "id": "A06-017",
        "source_ids": [
          "A06-017"
        ],
        "anchor": "A06-017",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Фильтр как обслуживаемая деталь",
        "y": 130.63967895507812,
        "page": 24,
        "old": 6,
        "id": "A06-018",
        "source_ids": [
          "A06-018"
        ],
        "anchor": "A06-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первый фильтр — сменная крупноплощадная кассета с известным материалом, а не случайный плотный поролон. Уплотнение по периметру важно: воздух проходит по пути меньшего сопротивления и может обойти фильтр через щели. Увеличение площади позволяет уменьшить скорость через материал и падение давления при том же расходе.",
        "y": 157.42721557617188,
        "page": 24,
        "old": 6,
        "id": "A06-019",
        "source_ids": [
          "A06-019"
        ],
        "anchor": "A06-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Кассета должна иметь ручку, направление установки и возможность очистки без попадания пыли на плату. Магнитное удержание допустимо для панели, если оно не заменяет механическую фиксацию тяжёлого узла. Не следует заявлять класс очистки воздуха всего изделия по характеристике одного фильтрующего листа.",
        "y": 225.23275756835938,
        "page": 24,
        "old": 6,
        "id": "A06-020",
        "source_ids": [
          "A06-020"
        ],
        "anchor": "A06-020",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Деталь",
            "Первая конструкция",
            "Приёмка"
          ],
          [
            "Рама",
            "Алюминиевый профиль",
            "Диагонали, жёсткость, люфт"
          ],
          [
            "Поддон платы",
            "Металл",
            "Стойки, изоляция, прогиб"
          ],
          [
            "Фильтр-кассета",
            "PETG/ASA",
            "Посадка, обход воздуха, нагрев"
          ],
          [
            "Радиозона",
            "Непроводящий пластик",
            "Связь в закрытом корпусе"
          ],
          [
            "Микрофонный держатель",
            "Печать + развязка",
            "Вибрации и резонансы"
          ]
        ],
        "y": 296.22,
        "page": 24,
        "old": 6,
        "id": "A06-021",
        "source_ids": [
          "A06-021"
        ],
        "anchor": "A06-021",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Тестовый купон вместо угадывания допусков",
        "y": 483.08013916015625,
        "page": 24,
        "old": 6,
        "id": "A06-022",
        "source_ids": [
          "A06-022"
        ],
        "anchor": "A06-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "До большой печати изготовить маленький образец с несколькими посадками под выбранную вставку, винт и защёлку. Измерить штангенциркулем, собрать и разобрать десять раз. Рабочие размеры перенести в CAD как параметры конкретного принтера, материала и ориентации.",
        "y": 501.5413513183594,
        "page": 24,
        "old": 6,
        "id": "A06-023",
        "source_ids": [
          "A06-023"
        ],
        "anchor": "A06-023",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Что можно отправить исполнителю",
        "y": 470.1255798339844,
        "page": 137,
        "old": 40,
        "id": "A40-030",
        "source_ids": [
          "A40-030"
        ],
        "anchor": "A40-030",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "После примерки — плоские панели с базами, размерами, материалом, толщиной, обработкой кромок, допусками и указанием покрытия. Для печати — модель, ориентация, материал, критичные размеры и контрольный купон. Для PCB — проверенные Gerber, drill, stackup и комплект схемы/спецификации. Иллюстрации этой книги не заменяют ни один из этих пакетов автоматически.",
        "y": 488.5867919921875,
        "page": 137,
        "old": 40,
        "id": "A40-031",
        "source_ids": [
          "A40-031"
        ],
        "anchor": "A40-031",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Распиновки, электрические ограничения и проектные размеры проверяются по фактическим деталям. Заготовка панели помогает начать примерку, но не является испытанным производственным чертежом. Уточнение программной архитектуры не меняет этого статуса.",
        "id": "I27-007",
        "old": "I27",
        "edited": true,
        "source_ids": [
          "I27-007"
        ],
        "anchor": "I27-007",
        "content_status": "active"
      },
      {
        "id": "R06-001",
        "kind": "h2",
        "text": "Металл и пластик: не назначать класс безопасности по названию",
        "source_ids": [
          "R06-001"
        ],
        "anchor": "R06-001",
        "content_status": "active"
      },
      {
        "id": "R06-002",
        "kind": "p",
        "text": "PETG, ASA, смола и фильтрующий поролон не получают единый класс UL 94 по названию материала. Он относится к конкретной марке, толщине и образцу; печать меняет геометрию и дефекты. Для горячих и силовых зон опорный вариант — металл либо материал с документацией на нужные условия. Фильтр держим вдали от горячего радиатора и не оставляем свободно свисающим. [174]",
        "source_ids": [
          "R06-002"
        ],
        "anchor": "R06-002",
        "content_status": "active"
      },
      {
        "id": "R06-003",
        "kind": "p",
        "text": "Если внутри куба установлен сетевой БП класса I, защитное соединение доступных металлических частей проектируется отдельно от сигнальной земли. Анодирование, краска и случайный контакт через винты не являются проверенным PE-соединением. Точки соединения, шайбы, проводники, затяжка и метод измерения вносятся в карту; численный предел задаётся применимым стандартом и выбранной конструкцией, а не универсальным советом «0,1 Ом мультиметром». Для E0 с внешними закрытыми адаптерами сеть внутрь самодельного корпуса не вводим.",
        "source_ids": [
          "R06-003"
        ],
        "anchor": "R06-003",
        "content_status": "active"
      }
    ],
    "origins": [
      "A06",
      [
        "A40",
        30,
        31
      ]
    ],
    "question": "Что лучше изготовить самому, а что заказать?",
    "intro": "Для первого экземпляра удобно разделить несущую часть и оболочку. Профиль или металлическая рама держит плату и тяжёлые детали. Съёмные панели задают вид, а печатные вставки удерживают фильтр, направляют воздух и закрывают сложные углы.\n\nТак можно менять форму без полной пересборки компьютера. Печатная деталь проверяется не только сразу после принтера: её нужно примерить после охлаждения, затяжки крепежа и нагрева в корпусе. Металлической панели, в свою очередь, нужны обработанные кромки и понятные электрические соединения.",
    "id": "ch-6",
    "slug": "chapter-06"
  },
  {
    "number": 7,
    "title": "Воздух должен пройти насквозь",
    "group": "Конструкция",
    "units": [
      {
        "kind": "lead",
        "text": "Два вентилятора на приток и один на вытяжку — исходная топология, а не доказательство достаточного охлаждения.",
        "y": 137.3555450439453,
        "page": 25,
        "old": 7,
        "id": "A07-001",
        "source_ids": [
          "A07-001"
        ],
        "anchor": "A07-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Активное охлаждение действительно упрощает первый куб: не нужно превращать весь корпус в точно рассчитанный радиатор. Но оно не отменяет теплотехнику. Процессору всё равно нужен собственный кулер, а воздуху — путь через горячие зоны. Три больших вентилятора могут оказаться менее полезны, чем два правильно размещённых, если воздух сразу выходит через ближайшую щель.",
        "y": 188.46142578125,
        "page": 25,
        "old": 7,
        "id": "A07-002",
        "source_ids": [
          "A07-002"
        ],
        "anchor": "A07-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Что переносит воздушный поток",
        "y": 284.2305908203125,
        "page": 25,
        "old": 7,
        "id": "A07-003",
        "source_ids": [
          "A07-003"
        ],
        "anchor": "A07-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для грубой оценки используется тепловой баланс:",
        "y": 311.24822998046875,
        "page": 25,
        "old": 7,
        "id": "A07-004",
        "source_ids": [
          "A07-004"
        ],
        "anchor": "A07-004",
        "content_status": "active"
      },
      {
        "kind": "quote",
        "text": "Q = P / (ρ · cₚ · ΔT)",
        "y": 355.1424865722656,
        "page": 25,
        "old": 7,
        "id": "A07-005",
        "source_ids": [
          "A07-005"
        ],
        "anchor": "A07-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Здесь Q — объёмный расход в м³/с, P — тепло, передаваемое воздуху, ρ — плотность воздуха, cₚ — его удельная теплоёмкость, ΔT — нагрев проходящего воздуха. При условных P = 120 Вт, ρ = 1,2 кг/м³, cₚ = 1005 Дж/(кг·К), ΔT = 10 К получается Q ≈ 0,00995 м³/с, то есть 35,8 м³/ч или около 21,1 CFM.",
        "y": 397.84686279296875,
        "page": 25,
        "old": 7,
        "id": "A07-006",
        "source_ids": [
          "A07-006"
        ],
        "anchor": "A07-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Это идеальный баланс потока, а не готовый номинал вентилятора. Он не учитывает неравномерность распределения, сопротивление фильтра, локальные застойные зоны и тепловое сопротивление между кристаллом и воздухом. Температура CPU зависит не только от выходного воздуха: важны контакт, радиатор, паста, монтаж и мощность кристалла.",
        "y": 470.25079345703125,
        "page": 25,
        "old": 7,
        "id": "A07-007",
        "source_ids": [
          "A07-007"
        ],
        "anchor": "A07-007",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Рабочая точка вентилятора",
        "y": 566.02001953125,
        "page": 25,
        "old": 7,
        "id": "A07-008",
        "source_ids": [
          "A07-008"
        ],
        "anchor": "A07-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Паспортный свободный расход измеряется почти без сопротивления, максимальное давление — почти без расхода. В корпусе одновременно не получаются оба максимума. Реальный режим находится на пересечении кривой вентилятора и сопротивления системы. Загрязнение фильтра сдвигает это пересечение в сторону меньшего расхода. [4] [75]",
        "y": 593.03759765625,
        "page": 25,
        "old": 7,
        "id": "A07-009",
        "source_ids": [
          "A07-009"
        ],
        "anchor": "A07-009",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A07",
        "caption": "Иллюстрация рабочей точки. Кривые нарисованы для объяснения механики и не воспроизводят паспорт конкретного вентилятора.",
        "y": 56.69291687011719,
        "page": 26,
        "old": 7,
        "id": "A07-010",
        "source_ids": [
          "A07-010"
        ],
        "anchor": "A07-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Два вентилятора рядом работают приблизительно как параллельные источники: при одинаковом перепаде давления увеличивается доступный расход. Два последовательно расположенных могут увеличить доступное давление, но взаимное влияние, расстояние и неоднородность потока мешают простому сложению. Для первого куба последовательная «турбина» из вентиляторов не нужна.",
        "y": 400.2798156738281,
        "page": 26,
        "old": 7,
        "id": "A07-011",
        "source_ids": [
          "A07-011"
        ],
        "anchor": "A07-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Слегка положительное давление внутри полезно, если приток фильтруется: через часть щелей воздух тогда выходит, а не втягивает пыль. Но количество вентиляторов не доказывает знак давления. Два медленных приточных за плотным фильтром могут уступать одной быстрой вытяжке.",
        "y": 488.65875244140625,
        "page": 26,
        "old": 7,
        "id": "A07-012",
        "source_ids": [
          "A07-012"
        ],
        "anchor": "A07-012",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Схема Koobique 320",
        "y": 568.4529418945312,
        "page": 26,
        "old": 7,
        "id": "A07-013",
        "source_ids": [
          "A07-013"
        ],
        "anchor": "A07-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Передние 120-мм вентиляторы подают воздух через общую кассету. Верхний поток направляется к CPU-кулеру; нижний обдувает плату, накопитель и зону поддона. Верхняя задняя вытяжка выводит нагретый воздух, не засасывая его обратно к переднему фильтру. БП получает свой предусмотренный производителем воздух и не превращается в единственный вытяжной канал всего куба.",
        "y": 595.4705810546875,
        "page": 26,
        "old": 7,
        "id": "A07-014",
        "source_ids": [
          "A07-014"
        ],
        "anchor": "A07-014",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A08",
        "caption": "Предлагаемые потоки в боковом разрезе. Расположение перегородок уточняется по температурным измерениям, а не только по стрелкам на схеме.",
        "y": 56.69291687011719,
        "page": 27,
        "old": 7,
        "id": "A07-015",
        "source_ids": [
          "A07-015"
        ],
        "anchor": "A07-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Воздуховод не должен полностью отрезать VRM или SSD ради красивой струи на CPU. Кожух вокруг кулера полезен лишь после сравнения температур остальных узлов. Размещение блока питания снизу требует доступа к его входу; нижний фильтр и ножки не должны перекрываться столом.",
        "y": 416.248291015625,
        "page": 27,
        "old": 7,
        "id": "A07-016",
        "source_ids": [
          "A07-016"
        ],
        "anchor": "A07-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Обычный и reverse-blade вентилятор",
        "y": 496.0424499511719,
        "page": 27,
        "old": 7,
        "id": "A07-017",
        "source_ids": [
          "A07-017"
        ],
        "anchor": "A07-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Обратная геометрия лопастей позволяет получить другое направление потока относительно видимой стороны рамки. Это удобно в прозрачных корпусах, где хотят видеть одну декоративную сторону. Она не делает вентилятор автоматически реверсивным по команде и не улучшает охлаждение сама по себе. У Lian Li такие модели выделены отдельными изделиями. [76]",
        "y": 523.0601196289062,
        "page": 27,
        "old": 7,
        "id": "A07-018",
        "source_ids": [
          "A07-018"
        ],
        "anchor": "A07-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для Koobique можно использовать три обычных вентилятора, ориентируя их по стрелке потока на рамке. Переполюсовывать 12-вольтовое питание ради смены направления нельзя. Если выбран reverse-blade, он рассматривается как другая модель со своей кривой, током, кабелем и посадкой.",
        "y": 611.4390258789062,
        "page": 27,
        "old": 7,
        "id": "A07-019",
        "source_ids": [
          "A07-019"
        ],
        "anchor": "A07-019",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Профиль управления",
        "y": 691.2332153320312,
        "page": 27,
        "old": 7,
        "id": "A07-020",
        "source_ids": [
          "A07-020"
        ],
        "anchor": "A07-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "На первом стенде предпочтительны штатные fan headers платы и UEFI-профиль, который работает даже при остановке Rust-core. Нижняя граница оборотов выбирается выше устойчивого запуска конкретного вентилятора. Температуру управления сглаживают, но перегрев должен иметь быстрый обход сглаживания.",
        "y": 718.2509155273438,
        "page": 27,
        "old": 7,
        "id": "A07-021",
        "source_ids": [
          "A07-021"
        ],
        "anchor": "A07-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Пример проектной логики: спокойный режим удерживает постоянную небольшую скорость; рост температуры запускает плавную рампу; отказ датчика температуры либо службы управления переводит вентиляторы в заранее выбранный повышенный режим. Пороги берутся из результатов стенда и паспортных пределов узлов, а не из универсальной таблицы «нормальной температуры CPU».",
        "y": 57.42815399169922,
        "page": 28,
        "old": 7,
        "id": "A07-022",
        "source_ids": [
          "A07-022"
        ],
        "anchor": "A07-022",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Как испытать охлаждение",
        "y": 153.19732666015625,
        "page": 28,
        "old": 7,
        "id": "A07-023",
        "source_ids": [
          "A07-023"
        ],
        "anchor": "A07-023",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сначала измеряется комнатная температура и режим без корпуса, затем тот же набор нагрузки в корпусе. Одновременно записываются CPU, SSD, доступные датчики платы, обороты и температура входного/выходного воздуха. После выхода на установившийся режим выполняются распознавание речи, запись данных и сетевые команды. Тест только максимальной CPU-нагрузки не показывает акустическую пригодность голоса. Воздушные датчики располагаются в воспроизводимых точках: на входе, в зоне платы и после CPU-кулера. Положение, расстояние от горячих поверхностей и способ крепления записываются, чтобы повторный опыт сравнивал те же участки потока.",
        "y": 180.2149658203125,
        "page": 28,
        "old": 7,
        "id": "A07-024",
        "source_ids": [
          "A07-024"
        ],
        "anchor": "A07-024",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Затем проверяется уменьшенная проницаемость фильтра безопасной контролируемой вставкой, без распыления пыли и без полного перекрытия БП. Учитываются накопленный нагрев и восстановление. Нельзя останавливать крыльчатку пальцем или оставлять заблокированный вентилятор без контроля.",
        "y": 268.5939025878906,
        "page": 28,
        "old": 7,
        "id": "A07-025",
        "source_ids": [
          "A07-025"
        ],
        "anchor": "A07-025",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Критерий принятия корпуса",
        "y": 357.2677307128906,
        "page": 28,
        "old": 7,
        "id": "A07-026",
        "source_ids": [
          "A07-026"
        ],
        "anchor": "A07-026",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "При выбранной нагрузке все узлы остаются в паспортных пределах с инженерным запасом; отсутствует непрерывное снижение частоты из-за тепла; управление сохраняет целевую задержку; фильтр можно извлечь; нет заметной рециркуляции горячего воздуха. Численные пределы фиксируются после выбора деталей.",
        "y": 375.7289123535156,
        "page": 28,
        "old": 7,
        "id": "A07-027",
        "source_ids": [
          "A07-027"
        ],
        "anchor": "A07-027",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Тепловой пример",
        "y": 653.9788208007812,
        "page": 135,
        "old": 40,
        "id": "A40-012",
        "source_ids": [
          "A40-012"
        ],
        "anchor": "A40-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Пусть суммарное тепло в основном отсеке принято равным 120 Вт, разрешённый рост температуры воздуха — 10 K. При условных плотности 1,2 кг/м³ и теплоёмкости 1005 Дж/(кг·K):",
        "y": 680.9964599609375,
        "page": 135,
        "old": 40,
        "id": "A40-013",
        "source_ids": [
          "A40-013"
        ],
        "anchor": "A40-013",
        "content_status": "active"
      },
      {
        "kind": "quote",
        "text": "Q = P / (ρ · cₚ · ΔT) Q = 120 / (1,2 × 1005 × 10) ≈ 0,00995 м³/с Q ≈ 35,8 м³/ч ≈ 21,1 CFM",
        "y": 71.1741943359375,
        "page": 136,
        "old": 40,
        "id": "A40-014",
        "source_ids": [
          "A40-014"
        ],
        "anchor": "A40-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Это баланс воздуха в идеализированном отсеке, а не паспортный выбор вентилятора. Он не учитывает локальные горячие зоны, рециркуляцию, фильтр, утечки и отдельное тепловое сопротивление CPU-кулера. Реальный расход находится на пересечении характеристик вентиляторов и тракта. Тестируется вся собранная система, не только сумма свободных CFM.",
        "y": 149.07855224609375,
        "page": 136,
        "old": 40,
        "id": "A40-015",
        "source_ids": [
          "A40-015"
        ],
        "anchor": "A40-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Если входной воздух уже тёплый, тот же перепад не гарантирует допустимую температуру SSD или VRM. Отдельно фиксируются температура помещения, температуры узлов, мощность нагрузки и обороты. Сравнение «до/после фильтра» без одинаковой нагрузки и окружающей температуры вводит в заблуждение.",
        "y": 237.45748901367188,
        "page": 136,
        "old": 40,
        "id": "A40-016",
        "source_ids": [
          "A40-016"
        ],
        "anchor": "A40-016",
        "content_status": "active"
      },
      {
        "id": "R07-001",
        "kind": "h2",
        "text": "Тепловой допуск — для конкретных деталей",
        "source_ids": [
          "R07-001"
        ],
        "anchor": "R07-001",
        "content_status": "active"
      },
      {
        "id": "R07-002",
        "kind": "p",
        "text": "Порог 85 °C для любого CPU и 70 °C для любого NVMe не универсален. В карте фиксируются паспортные рабочие пределы конкретных моделей, положение датчика и выбранный запас. Начальная цель: не менее 10 K до паспортного ограничения, отсутствие throttling в требуемой нагрузке и ΔT выходящего воздуха не выше 15 K. Это инженерные цели, а не нормы безопасности. В испытании нужны 2 часа нагрузки после теплового установления при измеренной комнатной температуре; затем тот же опыт с загрязнённым фильтром в согласованном состоянии.",
        "source_ids": [
          "R07-002"
        ],
        "anchor": "R07-002",
        "content_status": "active"
      },
      {
        "id": "R07-003",
        "kind": "p",
        "text": "Два притока и одна вытяжка сами по себе не доказывают избыточное давление. Корпус испытывают вместе с решётками, фильтром, поддоном и кабелями. Временно сравнивают точки температуры и перепада давления; оценка «60% потока в нижнем отсеке» по рисунку не используется. При остановке вентилятора проверяют весь переходный процесс и завершение нагрузки, не только сообщение тахометра.",
        "source_ids": [
          "R07-003"
        ],
        "anchor": "R07-003",
        "content_status": "active"
      }
    ],
    "origins": [
      "A07",
      [
        "A40",
        12,
        16
      ]
    ],
    "question": "Почему три вентилятора ещё не означают хорошее охлаждение?",
    "intro": "Вентилятор не задаёт расход независимо от корпуса. Он работает против сопротивления фильтра, решётки, кабелей и узких проходов. Поэтому два одинаковых вентилятора в разных корпусах могут перемещать разное количество воздуха.\n\nНаша задача — провести воздух через горячие зоны, не дать ему сразу вернуться ко входу и сохранить приемлемый режим после загрязнения фильтра. Расчёт даёт первый ориентир; измерение собранного тракта показывает, получилось ли это в действительности.",
    "id": "ch-7",
    "slug": "chapter-07"
  },
  {
    "number": 8,
    "title": "Вентиляторы без догадок",
    "group": "Конструкция",
    "units": [
      {
        "kind": "lead",
        "text": "Управляющий GPIO передаёт информацию. Ток двигателя идёт по отдельному силовому пути.",
        "y": 168.675537109375,
        "page": 29,
        "old": 8,
        "id": "A08-001",
        "source_ids": [
          "A08-001"
        ],
        "anchor": "A08-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "У типичного четырёхпроводного PC-вентилятора есть питание, земля, тахометрический выход и PWM-вход. У трёхпроводного скорость обычно меняется напряжением питания; у четырёхпроводного питание остаётся постоянным, а скорость задаётся отдельным сигналом. Цвета кабелей нельзя считать универсальной распиновкой.",
        "y": 219.78143310546875,
        "page": 29,
        "old": 8,
        "id": "A08-002",
        "source_ids": [
          "A08-002"
        ],
        "anchor": "A08-002",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A09",
        "caption": "Разделение силового и управляющего интерфейсов. Для первого куба собственный транзисторный контроллер необязателен: штатный header платы уменьшает число рисков.",
        "y": 299.9540710449219,
        "page": 29,
        "old": 8,
        "id": "A08-003",
        "source_ids": [
          "A08-003"
        ],
        "anchor": "A08-003",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Электрический интерфейс",
        "y": 56.314456939697266,
        "page": 30,
        "old": 8,
        "id": "A08-004",
        "source_ids": [
          "A08-004"
        ],
        "anchor": "A08-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В документации Noctua для PWM-моделей описаны целевая частота около 25 кГц, отдельный тахометрический выход и ограничения входов. Внутри одного семейства встречаются вентиляторы на 5, 12 и 24 В — одинаковое число проводов не означает одинаковое питание. [6]",
        "y": 83.33208465576172,
        "page": 30,
        "old": 8,
        "id": "A08-005",
        "source_ids": [
          "A08-005"
        ],
        "anchor": "A08-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Самый безопасный первый путь — использовать разъёмы материнской платы и её режим PWM. Если хочется собственный контроллер на MCU, PWM формируется через открытый сток/коллектор с общей сигнальной землёй, а электрические уровни сверяются с выбранной моделью. Не следует подключать вход, подтянутый внутри вентилятора к неизвестному уровню, непосредственно к незащищённому GPIO.",
        "y": 155.73602294921875,
        "page": 30,
        "old": 8,
        "id": "A08-006",
        "source_ids": [
          "A08-006"
        ],
        "anchor": "A08-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Тахометрический сигнал у рассматриваемых моделей имеет открытый коллектор и обычно два импульса за оборот. Для измерительного интервала Δt, числа импульсов N и коэффициента p импульсов/оборот скорость равна RPM = 60 · N / (p · Δt). При p = 2 и 40 импульсах за секунду получается 1200 об/мин. Коэффициент берётся из документации, а не зашивается глобально для всех моделей. [6]",
        "y": 244.11495971679688,
        "page": 30,
        "old": 8,
        "id": "A08-007",
        "source_ids": [
          "A08-007"
        ],
        "anchor": "A08-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "При низкой скорости удобнее измерять период между импульсами, а не считать их в коротком окне. Показание «0» различается с «датчик оборотов недоступен». Сигнал тахометра нельзя считать доказательством достаточного расхода: вентилятор может вращаться за забитым фильтром.",
        "y": 332.493896484375,
        "page": 30,
        "old": 8,
        "id": "A08-008",
        "source_ids": [
          "A08-008"
        ],
        "anchor": "A08-008",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Разветвители и пусковой ток",
        "y": 412.2880554199219,
        "page": 30,
        "old": 8,
        "id": "A08-009",
        "source_ids": [
          "A08-009"
        ],
        "anchor": "A08-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Токи вентиляторов складываются. Если три условных двигателя потребляют по 0,15 А в установившемся режиме, сумма составляет 0,45 А; при выборе header дополнительно учитывается запуск и запас. Эта арифметика не заменяет паспорт каждого вентилятора. Питание от отдельного хаба может разгрузить плату, но хаб тоже должен иметь документированные пределы.",
        "y": 439.30572509765625,
        "page": 30,
        "old": 8,
        "id": "A08-010",
        "source_ids": [
          "A08-010"
        ],
        "anchor": "A08-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "На одном тахометрическом входе разветвителя оставляют сигнал только одного вентилятора. Объединение нескольких выходов даёт смесь импульсов, а не среднюю скорость. Для диагностики всех трёх нужны отдельные каналы или специализированный контроллер.",
        "y": 527.6846313476562,
        "page": 30,
        "old": 8,
        "id": "A08-011",
        "source_ids": [
          "A08-011"
        ],
        "anchor": "A08-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Значение PWM = 0 не обязательно означает полную остановку, а отсутствие PWM не у всех изделий вызывает одинаковое поведение. Прошивка должна опираться на испытанную таблицу запуска, остановки и восстановления. После загрузки сначала включается безопасный режим вентиляции, затем разрешается тяжёлая вычислительная нагрузка.",
        "y": 600.0885620117188,
        "page": 30,
        "old": 8,
        "id": "A08-012",
        "source_ids": [
          "A08-012"
        ],
        "anchor": "A08-012",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Шум — не только число оборотов",
        "y": 56.314456939697266,
        "page": 31,
        "old": 8,
        "id": "A08-013",
        "source_ids": [
          "A08-013"
        ],
        "anchor": "A08-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Источники различаются: аэродинамический шум лопастей, турбулентность на решётке, вибрация панели, резонанс фильтра, тон двигателя и отражения внутри корпуса. Удаление одной слишком близкой решётки иногда полезнее замены вентилятора. Увеличение расстояния до препятствия или скругление воздухозаборника проверяется сравнением, а не воспринимается как гарантированное улучшение.",
        "y": 83.33208465576172,
        "page": 31,
        "old": 8,
        "id": "A08-014",
        "source_ids": [
          "A08-014"
        ],
        "anchor": "A08-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В рабочем кабинете начальный уровень шума не ограничен жёстко, но локальный голос делает акустику инженерным требованием. Станция должна распознавать команды именно тогда, когда её модель загрузила CPU и вентиляторы ускорились. Бенчмарк в тихом простое здесь недостаточен.",
        "y": 171.71102905273438,
        "page": 31,
        "old": 8,
        "id": "A08-015",
        "source_ids": [
          "A08-015"
        ],
        "anchor": "A08-015",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Подготовка к микрофонному модулю",
        "y": 251.50521850585938,
        "page": 31,
        "old": 8,
        "id": "A08-016",
        "source_ids": [
          "A08-016"
        ],
        "anchor": "A08-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первого этапа микрофон Fifine располагается отдельно от куба. Он создаёт эталонный путь захвата. Затем можно сравнивать выносной USB-массив и встроенную микрофонную «корону» в пластиковой верхней зоне.",
        "y": 278.5228576660156,
        "page": 31,
        "old": 8,
        "id": "A08-017",
        "source_ids": [
          "A08-017"
        ],
        "anchor": "A08-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Микрофоны не ставят в прямую струю, за пыльным плотным фильтром или на панель, механически связанную с вентилятором без развязки. Акустическая ткань, мембрана и форма отверстия изменяют тракт и требуют проверки. Герметичное красивое отверстие для MEMS может сделать весь массив хуже дешёвого внешнего микрофона.",
        "y": 334.9517822265625,
        "page": 31,
        "old": 8,
        "id": "A08-018",
        "source_ids": [
          "A08-018"
        ],
        "anchor": "A08-018",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Совместный тест голоса и вентиляции",
        "y": 423.6255798339844,
        "page": 31,
        "old": 8,
        "id": "A08-019",
        "source_ids": [
          "A08-019"
        ],
        "anchor": "A08-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Записать одни и те же русские и английские команды с фиксированных расстояний: тихий режим, длительная нагрузка CPU, воспроизведение ответа и работа вентилятора на повышенной скорости. Сравнить ошибки намерений, а не только субъективную приятность записи. По возможности сохранять эталонный текст и хеш аудиофайла.",
        "y": 442.0867919921875,
        "page": 31,
        "old": 8,
        "id": "A08-020",
        "source_ids": [
          "A08-020"
        ],
        "anchor": "A08-020",
        "content_status": "active"
      },
      {
        "id": "R08-001",
        "kind": "h2",
        "text": "Тахометр не объединяется так же, как питание",
        "source_ids": [
          "R08-001"
        ],
        "anchor": "R08-001",
        "content_status": "active"
      },
      {
        "id": "R08-002",
        "kind": "p",
        "text": "PWM-выход MCU управляет входом вентилятора через открытый сток; выход 3,3 В нельзя напрямую соединять с непроверенной внутренней подтяжкой 5 В. Тахолинию подтягивают к допустимому для приёмника уровню, обычно 3,3 В для MCU, но не к 12 В. Несколько таховыходов не замыкают вместе. Готовый разветвитель должен явно указывать, какой вентилятор измеряется; отдельная диагностика требует отдельных входов. [6]",
        "source_ids": [
          "R08-002"
        ],
        "anchor": "R08-002",
        "content_status": "active"
      }
    ],
    "origins": [
      "A08"
    ],
    "question": "Что означают четыре провода и обратные лопасти?",
    "intro": "У корпусного вентилятора отдельно существуют питание, команда скорости и обратная связь об оборотах. Их нельзя произвольно соединять с выводами микроконтроллера. Сначала используем штатный разъём платы или документированный контроллер; собственный интерфейс собираем только по электрическому описанию.\n\nВентилятор с обратными лопастями позволяет получить нужный поток при другом внешнем виде. Это не обязательная часть приточно-вытяжной схемы: направление проверяется по конкретной модели и её маркировке.",
    "id": "ch-8",
    "slug": "chapter-08"
  },
  {
    "number": 9,
    "title": "Питание от входа до нагрузки",
    "group": "Электроника",
    "units": [
      {
        "kind": "lead",
        "text": "Стабильное питание и надёжные контакты нужны до отладки прошивки. Просадки напряжения и ошибки разъёмов устраняются в электрической части.",
        "y": 168.675537109375,
        "page": 32,
        "old": 9,
        "id": "A09-001",
        "source_ids": [
          "A09-001"
        ],
        "anchor": "A09-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первый Koobique использует готовые закрытые источники питания: штатный БП вычислителя, USB-питание отладочных плат и отдельный 12-вольтовый адаптер низковольтной нагрузки. Это позволяет изучить управление реле, не проектируя одновременно сетевой преобразователь и защиту от поражения током.",
        "y": 219.78143310546875,
        "page": 32,
        "old": 9,
        "id": "A09-002",
        "source_ids": [
          "A09-002"
        ],
        "anchor": "A09-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Разделение силовых цепей",
        "y": 299.5755920410156,
        "page": 32,
        "old": 9,
        "id": "A09-003",
        "source_ids": [
          "A09-003"
        ],
        "anchor": "A09-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "ATX и EPS питают материнскую плату и CPU. SATA или штатные периферийные разъёмы используются только по назначению. Четырёхпроводные вентиляторы подключаются к рассчитанным fan headers или проверенному хабу. USB-плата получает разрешённый входной уровень, а её локальный стабилизатор формирует питание микроконтроллера.",
        "y": 326.5932312011719,
        "page": 32,
        "old": 9,
        "id": "A09-004",
        "source_ids": [
          "A09-004"
        ],
        "anchor": "A09-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Модульные кабели БП не взаимозаменяемы по одному внешнему сходству разъёма. CPU/EPS и PCIe также нельзя смешивать. В первом экземпляре применяются кабели из комплекта конкретного БП; удлинители и переходники добавляются только после проверки распиновки и допустимого тока.",
        "y": 414.97216796875,
        "page": 32,
        "old": 9,
        "id": "A09-005",
        "source_ids": [
          "A09-005"
        ],
        "anchor": "A09-005",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A10",
        "caption": "Независимое питание Hub, логики узлов и нагрузки. Реле питается от 5 В своего узла; USB станции не является его штатным источником.",
        "y": 56.69291687011719,
        "page": 33,
        "old": 9,
        "id": "A09-006",
        "source_ids": [
          "A09-006"
        ],
        "anchor": "A09-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Питание ESP32-C6",
        "y": 412.6464538574219,
        "page": 33,
        "old": 9,
        "id": "A09-007",
        "source_ids": [
          "A09-007"
        ],
        "anchor": "A09-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Руководство DevKitC-1 различает USB-питание и питание через контакты 5V или 3V3. Эти способы нельзя произвольно объединять. В документации также описана перемычка для измерения тока модуля; её поведение отличается от измерения потребления всей отладочной платы с LED и USB-мостом. [14]",
        "y": 439.6640625,
        "page": 33,
        "old": 9,
        "id": "A09-008",
        "source_ids": [
          "A09-008"
        ],
        "anchor": "A09-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Плата с установленным Wi-Fi при передаче потребляет ток импульсами. Измерение мультиметром усредняет эти события, поэтому просадку у питания MCU полезно смотреть осциллографом. Симптомы недостаточного питания — перезапуски при установлении связи, ошибки TLS под нагрузкой и нестабильные датчики — легко ошибочно принять за программную проблему.",
        "y": 512.0679931640625,
        "page": 33,
        "old": 9,
        "id": "A09-009",
        "source_ids": [
          "A09-009"
        ],
        "anchor": "A09-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Отдельно учитывается падение напряжения кабеля. Для условного двухпроводного участка с суммарным сопротивлением 0,4 Ω и током 0,5 А потеря составляет 0,2 В, а нагрев — 0,1 Вт. Этот пример не задаёт допустимый кабель: фактическое сопротивление включает провода, контакты и переходники.",
        "y": 600.4468994140625,
        "page": 33,
        "old": 9,
        "id": "A09-010",
        "source_ids": [
          "A09-010"
        ],
        "anchor": "A09-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Защита 12-вольтовой нагрузки",
        "y": 680.2411499023438,
        "page": 33,
        "old": 9,
        "id": "A09-011",
        "source_ids": [
          "A09-011"
        ],
        "anchor": "A09-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Предохранитель располагается близко к источнику, до длинного провода. Его выбирают по рабочему и пусковому току, допустимому току кабеля и способности прервать ток на постоянном напряжении. Слово «12 В» не делает короткое замыкание безопасным: источник может отдавать большой ток и перегревать тонкий провод.",
        "y": 707.2587280273438,
        "page": 33,
        "old": 9,
        "id": "A09-012",
        "source_ids": [
          "A09-012"
        ],
        "anchor": "A09-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первый светильник удобно ограничить мощностью порядка 6 Вт при 12 В — около 0,5 А в установившемся режиме. Это проектный пример, не обязательная модель покупки. Нагрузка должна быть рассчитана на прямое питание выбранным адаптером; голые мощные светодиоды без драйвера сюда не относятся.",
        "y": 57.42815399169922,
        "page": 34,
        "old": 9,
        "id": "A09-013",
        "source_ids": [
          "A09-013"
        ],
        "anchor": "A09-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сигнальная земля узла и земля драйвера катушки обычно объединяются. Сухие контакты реле могут переключать отдельную цепь лампы без соединения её минуса с MCU. Если добавляется неразвязанный измеритель тока или напряжения, топология земли меняется и должна быть показана на схеме, а не появляться случайно через USB-анализатор.",
        "y": 129.83209228515625,
        "page": 34,
        "old": 9,
        "id": "A09-014",
        "source_ids": [
          "A09-014"
        ],
        "anchor": "A09-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Жгут как инженерная деталь",
        "y": 225.6012725830078,
        "page": 34,
        "old": 9,
        "id": "A09-015",
        "source_ids": [
          "A09-015"
        ],
        "anchor": "A09-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Каждый кабель имеет обозначение, оба конца и назначение контактов. Например, W-S01: Sense J1 → SHT45, сигналы 3V3/GND/SDA/SCL. Цвет помогает монтажу, но не заменяет таблицу. Разъёмы выбираются с ключом или явной механической защитой от переворота.",
        "y": 252.618896484375,
        "page": 34,
        "old": 9,
        "id": "A09-016",
        "source_ids": [
          "A09-016"
        ],
        "anchor": "A09-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для прототипа Dupont допустим на столе, но постоянный экземпляр лучше перевести на фиксируемые разъёмы, паяную макетную плату или PCB-носитель. Клемма должна соответствовать сечению провода; многопроволочную жилу обрабатывают способом, предусмотренным клеммой. Лужёный конец под винтом не стоит считать универсально надёжным соединением из-за деформации припоя со временем.",
        "y": 325.0228576660156,
        "page": 34,
        "old": 9,
        "id": "A09-017",
        "source_ids": [
          "A09-017"
        ],
        "anchor": "A09-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Силовые провода не натягивают через острые кромки. Разгрузка натяжения располагается до платы. Отверстие в металле получает втулку или иной защитный элемент. Провод вентилятора фиксируется так, чтобы при снятии панели он не попал в крыльчатку.",
        "y": 413.40179443359375,
        "page": 34,
        "old": 9,
        "id": "A09-018",
        "source_ids": [
          "A09-018"
        ],
        "anchor": "A09-018",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Металл, земля и защитный проводник",
        "y": 477.2209777832031,
        "page": 34,
        "old": 9,
        "id": "A09-019",
        "source_ids": [
          "A09-019"
        ],
        "anchor": "A09-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Защитное соединение корпуса и сигнальная GND — разные задачи. Если в изделии есть сетевой БП класса I, доступные металлические части и его монтаж должны соответствовать требованиям защитного заземления и инструкции производителя. При полностью внешнем изолированном источнике низкого напряжения схема иная. Нельзя заменять анализ безопасности универсальной перемычкой «весь металл на GND платы».",
        "y": 504.23858642578125,
        "page": 34,
        "old": 9,
        "id": "A09-020",
        "source_ids": [
          "A09-020"
        ],
        "anchor": "A09-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первого экземпляра сетевой ввод оставляют в штатном закрытом БП либо полностью снаружи. Собственный IEC-ввод, выключатель сети и внутренние сетевые соединения требуют отдельного квалифицированного проектирования. Их отсутствие не мешает ни кубической форме, ни качеству программной платформы.",
        "y": 608.592529296875,
        "page": 34,
        "old": 9,
        "id": "A09-021",
        "source_ids": [
          "A09-021"
        ],
        "anchor": "A09-021",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Первое включение узла",
        "y": 697.266357421875,
        "page": 34,
        "old": 9,
        "id": "A09-022",
        "source_ids": [
          "A09-022"
        ],
        "anchor": "A09-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Проверить отсутствие короткого замыкания при снятом питании. Отдельно проверить напряжение адаптера. Включить узел без нагрузки, затем с ограниченной низковольтной нагрузкой. Измерить ток и температуру соединений. В журнал записать источник, кабель, ревизию платы и поведение при перезапуске.",
        "y": 715.7274780273438,
        "page": 34,
        "old": 9,
        "id": "A09-023",
        "source_ids": [
          "A09-023"
        ],
        "anchor": "A09-023",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Падение напряжения в жгуте",
        "y": 317.25164794921875,
        "page": 136,
        "old": 40,
        "id": "A40-017",
        "source_ids": [
          "A40-017"
        ],
        "anchor": "A40-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для учебного расчёта примем медный двухпроводный путь длиной 1 м до нагрузки и 1 м обратно, сечением 0,5 мм², удельным сопротивлением 0,0175 Ом·мм²/м при опорных условиях. Это приблизительная исходная величина; реальный провод и нагрев проверяются измерением.",
        "y": 344.269287109375,
        "page": 136,
        "old": 40,
        "id": "A40-018",
        "source_ids": [
          "A40-018"
        ],
        "anchor": "A40-018",
        "content_status": "active"
      },
      {
        "kind": "quote",
        "text": "Rпетли = ρ · 2L / S ≈ 0,0175 × 2 / 0,5 = 0,07 Ом При 2 А: ΔV ≈ 0,14 В; Pпровода ≈ 0,28 Вт",
        "y": 436.0885314941406,
        "page": 136,
        "old": 40,
        "id": "A40-019",
        "source_ids": [
          "A40-019"
        ],
        "anchor": "A40-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "К этому добавляются разъёмы, контакт реле, предохранитель и клеммы. Для 12-вольтовой лампы небольшой провал может быть допустим, а для короткого пика Wi-Fi на слабой 3,3-вольтовой шине — вызвать reset. Поэтому измеряется минимум напряжения у потребителя во время нагрузки, не только выход адаптера мультиметром в покое.",
        "y": 496.3929443359375,
        "page": 136,
        "old": 40,
        "id": "A40-020",
        "source_ids": [
          "A40-020"
        ],
        "anchor": "A40-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Номинал предохранителя выбирается по проводу, пусковому току, нагрузке и времятоковой характеристике. Он не получается автоматически из среднего тока лампы и не предназначен для защиты GPIO.",
        "y": 568.796875,
        "page": 136,
        "old": 40,
        "id": "A40-021",
        "source_ids": [
          "A40-021"
        ],
        "anchor": "A40-021",
        "content_status": "active"
      },
      {
        "id": "R09-001",
        "kind": "h2",
        "text": "Независимость от станции начинается с питания",
        "source_ids": [
          "R09-001"
        ],
        "anchor": "R09-001",
        "content_status": "active"
      },
      {
        "id": "R09-002",
        "kind": "p",
        "text": "Actuator питается не от USB Hub, а от собственного закрытого источника 5 В. Катушка документированного 5-вольтового модуля подключается к его VIN 5 В, не к 3,3 В DevKit. Sense и Presence также получают независимое питание, если заявлена работа при выключенном Hub. Для отладки через ПК допускается временная связь, но она отмечается как зависимость стенда. Одновременные источники на разъёмах DevKit не объединяют без проверки схемы платы и обратного питания.",
        "source_ids": [
          "R09-002"
        ],
        "anchor": "R09-002",
        "content_status": "active"
      },
      {
        "id": "R09-003",
        "kind": "p",
        "text": "Для E0-L6 предлагаем лампу 12 В DC до 6 Вт, провод от 0,5 мм² и F1-кандидат 1 А с DC-рейтингом не ниже 32 В рядом с плюсом источника. Это не утверждённая защитная координация: до подключения фиксируются точная лампа, DC-адаптер, предельный и пусковой ток, время-токовая кривая F1 и отключение источника при КЗ. Если адаптер ограничивает ток ниже срабатывания F1, предохранитель не считается гарантированно разрывающим КЗ — проверяется собственная защита источника. MR16 и надпись «12 В» не доказывают постоянное напряжение.",
        "source_ids": [
          "R09-003"
        ],
        "anchor": "R09-003",
        "content_status": "active"
      },
      {
        "id": "R09-004",
        "kind": "p",
        "text": "Физическое отключение питания нагрузки остаётся доступным независимо от MCU. Кнопка на GPIO не размыкает сварившийся контакт реле. Стенд ограничен световой нагрузкой, не переносится на инструмент, нагреватель или замок. Переход к сетевой нагрузке потребует отдельного электрического проекта, готовых закрытых аппаратов и компетентной проверки.",
        "source_ids": [
          "R09-004"
        ],
        "anchor": "R09-004",
        "content_status": "active"
      }
    ],
    "origins": [
      "A09",
      [
        "A40",
        17,
        21
      ]
    ],
    "question": "Куда уходит энергия и где искать просадку напряжения?",
    "intro": "На бумаге источник выдаёт 12 В. На столе между ним и лампой стоят провод, разъём, предохранитель, контакт реле и ещё один провод. Каждый участок меняет условия у нагрузки. Для микроконтроллера короткая просадка может оказаться важнее среднего показания мультиметра.\n\nВ первом стенде отделяем готовое сетевое питание от доступных низковольтных цепей. Работу со вскрытым сетевым блоком питания не используем как учебный этап. Проверяем полярность, возвратный путь тока, крепление проводов и поведение в момент включения.",
    "id": "ch-9",
    "slug": "chapter-09"
  },
  {
    "number": 10,
    "title": "Wi-Fi в обычном доме",
    "group": "Связь",
    "units": [
      {
        "kind": "lead",
        "text": "Основной путь пользователя — обычная домашняя Wi-Fi-сеть. Отдельная сеть хаба остаётся сервисным или будущим специализированным вариантом.",
        "y": 168.675537109375,
        "page": 35,
        "old": 10,
        "id": "A10-001",
        "source_ids": [
          "A10-001"
        ],
        "anchor": "A10-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Станция подключается к роутеру как клиент. Телефон остаётся в привычной сети. Узлы первого комплекта подключаются к доступной сети 2,4 ГГц, а станция может работать в другом диапазоне, если роутер допускает локальное взаимодействие между ними. Облачный DNS и интернет не участвуют в передаче команд внутри этой сети.",
        "y": 219.5513153076172,
        "page": 35,
        "old": 10,
        "id": "A10-002",
        "source_ids": [
          "A10-002"
        ],
        "anchor": "A10-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Радио на Linux",
        "y": 292.9929504394531,
        "page": 35,
        "old": 10,
        "id": "A10-003",
        "source_ids": [
          "A10-003"
        ],
        "anchor": "A10-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Покупать Wi-Fi-модуль нужно по чипу и драйверу, а не по надписи «Wi-Fi 6». Linux iwlwifi документирует поддерживаемые устройства и ограничения режимов, включая различия client/AP. Наличие хорошего клиентского режима не гарантирует полноценную точку доступа на любом диапазоне. [38]",
        "y": 319.78045654296875,
        "page": 35,
        "old": 10,
        "id": "A10-004",
        "source_ids": [
          "A10-004"
        ],
        "anchor": "A10-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первой станции достаточно надёжного Wi-Fi-клиента и Bluetooth для дальнейшего сопряжения. Второй независимый радиомодуль имеет смысл, если потребуется собственная сеть устройств без зависимости от домашнего роутера. Он не должен появляться как скрытое обязательное условие приложения.",
        "y": 387.58599853515625,
        "page": 35,
        "old": 10,
        "id": "A10-005",
        "source_ids": [
          "A10-005"
        ],
        "anchor": "A10-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "У металлического куба антенны выносятся наружу или в специально предусмотренную радиопрозрачную область. Коаксиальный кабель, разъём и антенна рассматриваются как один тракт. Нельзя произвольно удлинять тонкий пигтейл и ожидать прежней чувствительности.",
        "y": 455.3915710449219,
        "page": 35,
        "old": 10,
        "id": "A10-006",
        "source_ids": [
          "A10-006"
        ],
        "anchor": "A10-006",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A11",
        "caption": "Металлическая оболочка и радио. Внешняя антенна или пластиковая зона устраняют очевидный экран, но не заменяют испытание расположения и согласования.",
        "y": 56.69291687011719,
        "page": 36,
        "old": 10,
        "id": "A10-007",
        "source_ids": [
          "A10-007"
        ],
        "anchor": "A10-007",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Антенны устройств",
        "y": 367.2290954589844,
        "page": 36,
        "old": 10,
        "id": "A10-008",
        "source_ids": [
          "A10-008"
        ],
        "anchor": "A10-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "ESP32-C6-WROOM-1 имеет печатную антенну, вариант WROOM-1U — разъём внешней. В готовом узле соблюдаются рекомендации по свободной зоне около антенны, земле и размещению металла. Плата, прекрасно работающая без корпуса, может потерять связь после установки батареи или винта перед антенной. [14][81]",
        "y": 394.0166320800781,
        "page": 36,
        "old": 10,
        "id": "A10-009",
        "source_ids": [
          "A10-009"
        ],
        "anchor": "A10-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первого Sense предпочтителен пластиковый корпус с вынесенной к краю антенной. Металлические декоративные элементы рядом с радио добавляются после базового измерения. Кабель датчика также может изменить электромагнитную обстановку, особенно если он длинный и идёт вдоль антенны.",
        "y": 461.8221740722656,
        "page": 36,
        "old": 10,
        "id": "A10-010",
        "source_ids": [
          "A10-010"
        ],
        "anchor": "A10-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Обнаружение и идентичность",
        "y": 535.2638549804688,
        "page": 36,
        "old": 10,
        "id": "A10-011",
        "source_ids": [
          "A10-011"
        ],
        "anchor": "A10-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Локальное обнаружение должно быть удобным, но не определять доверие. Приложение может использовать Bonjour/mDNS, адрес из QR или уже известный локальный endpoint. После обнаружения оно проверяет закреплённую идентичность станции. IP и имя — адреса, а не доказательство владельца.",
        "y": 562.0513305664062,
        "page": 36,
        "old": 10,
        "id": "A10-012",
        "source_ids": [
          "A10-012"
        ],
        "anchor": "A10-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Если multicast заблокирован, приложение должно уметь подключиться по известному IP или найти станцию через ограниченный механизм восстановления. Полное сканирование всей локальной сети не следует делать постоянной фоновой функцией. На iOS доступ к локальной сети требует предусмотренного разрешения и корректного объяснения пользователю. [57]",
        "y": 629.8568725585938,
        "page": 36,
        "old": 10,
        "id": "A10-013",
        "source_ids": [
          "A10-013"
        ],
        "anchor": "A10-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Что может сломать локальную связь",
        "y": 56.314456939697266,
        "page": 37,
        "old": 10,
        "id": "A10-014",
        "source_ids": [
          "A10-014"
        ],
        "anchor": "A10-014",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Условие",
            "Симптом",
            "Действие системы"
          ],
          [
            "Guest Wi-Fi / client isolation",
            "Телефон видит интернет, но не хаб",
            "Объяснить локальную изоляцию, не винить пароль"
          ],
          [
            "Только 5 ГГц для узла 2,4 ГГц",
            "Устройство не присоединяется",
            "Показать совместимый диапазон при настройке"
          ],
          [
            "Смена DHCP-адреса",
            "Сохранённый IP устарел",
            "Повторное обнаружение с проверкой прежнего ключа"
          ],
          [
            "Роутер перезапущен",
            "Сессии оборвались",
            "Переподключение с jitter и сверка состояния"
          ],
          [
            "Шумный эфир",
            "Повторные передачи и задержки",
            "Снизить телеметрию, показать качество канала"
          ],
          [
            "Ошибка DNS провайдера",
            "Внешние имена недоступны",
            "Локальный путь не обращается к ним"
          ]
        ],
        "y": 85.43,
        "page": 37,
        "old": 10,
        "id": "A10-015",
        "source_ids": [
          "A10-015"
        ],
        "anchor": "A10-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "WPA-защита сети не заменяет аутентификацию протокола. Гость, имеющий пароль Wi-Fi, не должен получать право управлять реле. С другой стороны, TLS не исправляет слабый радиосигнал и роутер, который запрещает обмен между клиентами.",
        "y": 365.5060729980469,
        "page": 37,
        "old": 10,
        "id": "A10-016",
        "source_ids": [
          "A10-016"
        ],
        "anchor": "A10-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Политика переподключения",
        "y": 423.9096984863281,
        "page": 37,
        "old": 10,
        "id": "A10-017",
        "source_ids": [
          "A10-017"
        ],
        "anchor": "A10-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Потеряв сеть, узел сохраняет локальную функцию и постепенно увеличивает интервал попыток с небольшим случайным разбросом. Одновременный старт десятков узлов не должен создавать синхронную лавину TLS-handshake. После подключения узел передаёт boot_id, последнюю версию состояния и актуальные наблюдения; исторические записи идут отдельно.",
        "y": 450.6972351074219,
        "page": 37,
        "old": 10,
        "id": "A10-018",
        "source_ids": [
          "A10-018"
        ],
        "anchor": "A10-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Постоянная доступность устройства не равна постоянной высокой частоте heartbeat. Интервал определяется допустимым временем обнаружения потери связи и энергопотреблением. Для стационарного Actuator он короче, для будущего спящего датчика — связан с расписанием пробуждения.",
        "y": 533.5408325195312,
        "page": 37,
        "old": 10,
        "id": "A10-019",
        "source_ids": [
          "A10-019"
        ],
        "anchor": "A10-019",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Как проверять радио",
        "y": 606.9824829101562,
        "page": 37,
        "old": 10,
        "id": "A10-020",
        "source_ids": [
          "A10-020"
        ],
        "anchor": "A10-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сохраняются положение станции, ориентация антенн, диапазон, расстояние, материал стен и нагрузка сети. Измеряются не только RSSI, но и частота переподключений, задержка команды, процент ошибок и поведение при переносе телефона. Одна удачная передача не подтверждает надёжность.",
        "y": 633.7699584960938,
        "page": 37,
        "old": 10,
        "id": "A10-021",
        "source_ids": [
          "A10-021"
        ],
        "anchor": "A10-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для кабинета полезны три точки: рядом со станцией, обычное рабочее место и худшее предполагаемое место датчика. Те же точки повторяются с закрытым корпусом, работающими вентиляторами и активным USB/SSD. Электромагнитные помехи от собственной конструкции должны выявляться до изготовления красивых панелей.",
        "y": 701.5755004882812,
        "page": 37,
        "old": 10,
        "id": "A10-022",
        "source_ids": [
          "A10-022"
        ],
        "anchor": "A10-022",
        "content_status": "active"
      },
      {
        "id": "R10-001",
        "kind": "h2",
        "text": "Wi-Fi остаётся основным режимом",
        "source_ids": [
          "R10-001"
        ],
        "anchor": "R10-001",
        "content_status": "active"
      },
      {
        "id": "R10-002",
        "kind": "p",
        "text": "Ethernet удобен как эталон для диагностики, но не подменяет согласованный пользовательский режим: Hub и узлы должны пройти приёмку через обычный Wi-Fi. Металлический куб получает наружные антенны либо проверенное радиопрозрачное окно. Сравниваются задержки и отказы с открытыми и закрытыми панелями; RSSI около −70 дБм — только диагностический ориентир, не доказательство пригодности.",
        "source_ids": [
          "R10-002"
        ],
        "anchor": "R10-002",
        "content_status": "active"
      },
      {
        "id": "R10-003",
        "kind": "p",
        "text": "Для постоянно питаемого Actuator в E0 выбирается WIFI_PS_NONE; экономию модема проверяют позднее как другую конфигурацию. На Linux фиксируется фактическая политика power-save адаптера. Результат зависит от AP, DTIM, диапазона и драйвера, поэтому не обещаем универсальную прибавку «100–300 мс». [178]",
        "source_ids": [
          "R10-003"
        ],
        "anchor": "R10-003",
        "content_status": "active"
      },
      {
        "id": "R10-004",
        "kind": "p",
        "text": "H0 запускается на Linux с доступной локальной сетью. WSL2, Docker Desktop и виртуальная машина допускаются лишь после отдельной проверки входящих соединений, multicast и проброса USB/BLE; NAT может скрыть проблему под видом неисправного устройства. Смена пароля Wi-Fi открывает физически подтверждённый сетевой provisioning без сброса владельца. Резерв питания только Hub не сохраняет Wi-Fi, если обесточен AP.",
        "source_ids": [
          "R10-004"
        ],
        "anchor": "R10-004",
        "content_status": "active"
      }
    ],
    "origins": [
      "A10"
    ],
    "question": "Как сохранить удобство и локальную независимость одновременно?",
    "intro": "Телефон остаётся в обычной домашней сети. Станция подключается к тому же роутеру по Wi-Fi, а устройства находят её внутри этой сети. Внешний DNS и облачный аккаунт не нужны для ежедневной команды.\n\nНо роутер всё ещё участвует в связи. Гостевая изоляция клиентов, новый пароль или отключённая точка доступа могут разорвать локальный путь. Поэтому локальность означает независимость от внешних сервисов, а не независимость от любого сетевого оборудования.",
    "id": "ch-10",
    "slug": "chapter-10"
  },
  {
    "number": 11,
    "title": "Маленький компьютер у датчика",
    "group": "Электроника",
    "units": [
      {
        "kind": "lead",
        "text": "Общая электронная основа ускоряет разработку. При её выборе сохраняется возможность разных режимов питания будущих устройств.",
        "y": 137.3555450439453,
        "page": 38,
        "old": 11,
        "id": "A11-001",
        "source_ids": [
          "A11-001"
        ],
        "anchor": "A11-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первых трёх узлов выбирается ESP32-C6-DevKitC-1. Это не обещание, что весь ассортимент Koobique останется на одном MCU. Отладочная плата удобна наличием питания, USB, загрузчика и готового радиомодуля. У варианта N8 документированы 8 МБ flash и отсутствие PSRAM; это важно при одновременной работе BLE, TLS и обновления. [14]",
        "y": 188.23130798339844,
        "page": 38,
        "old": 11,
        "id": "A11-002",
        "source_ids": [
          "A11-002"
        ],
        "anchor": "A11-002",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A12",
        "caption": "Проектное назначение сигналов первых узлов. Физическое положение контакта берётся из таблицы J1/J3 и сверяется с надписями реальной платы.",
        "y": 264.88604736328125,
        "page": 38,
        "old": 11,
        "id": "A11-003",
        "source_ids": [
          "A11-003"
        ],
        "anchor": "A11-003",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Роли одинаковой платы",
        "y": 623.3278198242188,
        "page": 38,
        "old": 11,
        "id": "A11-004",
        "source_ids": [
          "A11-004"
        ],
        "anchor": "A11-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "На Sense используются I²C-датчики. На Presence — цифровой вход PIR и UART радара. На Actuator — выход управления реле и физическая кнопка. Общая прошивочная инфраструктура обеспечивает сеть, идентичность, обновление и диагностику, но список драйверов и периферии задаётся профилем сборки.",
        "y": 650.1152954101562,
        "page": 38,
        "old": 11,
        "id": "A11-005",
        "source_ids": [
          "A11-005"
        ],
        "anchor": "A11-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Исполнитель не должен принимать конфигурацию, которая случайно назначает реле на загрузочный strapping pin. Карта контактов имеет аппаратную версию и проверяется при сборке. Изменение GPIO — изменение описания платы, а не пользовательский параметр обычной автоматизации.",
        "y": 717.9208374023438,
        "page": 38,
        "old": 11,
        "id": "A11-006",
        "source_ids": [
          "A11-006"
        ],
        "anchor": "A11-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Предлагаемая карта контактов",
        "y": 56.314456939697266,
        "page": 39,
        "old": 11,
        "id": "A11-007",
        "source_ids": [
          "A11-007"
        ],
        "anchor": "A11-007",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Функция",
            "GPIO / питание",
            "Контакт DevKitC-1",
            "Примечание"
          ],
          [
            "Питание датчиков",
            "3V3",
            "J1-1",
            "Проверить суммарный ток модулей"
          ],
          [
            "I²C SDA",
            "GPIO6",
            "J1-5",
            "Внешняя подтяжка к 3,3 В, с учётом breakout"
          ],
          [
            "I²C SCL",
            "GPIO7",
            "J1-6",
            "Начать со 100 кГц"
          ],
          [
            "Relay SIG",
            "GPIO10",
            "J1-10",
            "Внешний безопасный уровень при старте"
          ],
          [
            "Кнопка или PIR",
            "GPIO18",
            "J3-10",
            "Назначение зависит от профиля узла"
          ],
          [
            "UART RX MCU",
            "GPIO20",
            "J3-8",
            "Соединяется с TX радара"
          ],
          [
            "UART TX MCU",
            "GPIO21",
            "J3-7",
            "Соединяется с RX радара"
          ],
          [
            "Общая земля",
            "GND",
            "J1-15 / J3-1",
            "Не ориентироваться только по цвету"
          ]
        ],
        "y": 85.43,
        "page": 39,
        "old": 11,
        "id": "A11-008",
        "source_ids": [
          "A11-008"
        ],
        "anchor": "A11-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Таблица основана на опубликованной распиновке DevKitC-1; конкретная ревизия и совместимые клоны должны быть сверены. GPIO8 занят RGB LED, GPIO9 связан с загрузочным режимом, GPIO12/13 используются USB, GPIO16/17 — UART-мостом. GPIO4/5, 8/9 и 15 относятся к strapping-функциям и не выбираются произвольно для исполнительного выхода. [14]",
        "y": 444.3728332519531,
        "page": 39,
        "old": 11,
        "id": "A11-009",
        "source_ids": [
          "A11-009"
        ],
        "anchor": "A11-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "GPIO6/7 связаны также с внешним JTAG. При выбранной I²C-карте для отладки используется подходящий USB Serial/JTAG путь, а не одновременное подключение внешнего JTAG к тем же линиям. Конфликты периферии обнаруживаются в описании платы до разводки.",
        "y": 527.2163696289062,
        "page": 39,
        "old": 11,
        "id": "A11-010",
        "source_ids": [
          "A11-010"
        ],
        "anchor": "A11-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Периферийный контракт",
        "y": 585.6200561523438,
        "page": 39,
        "old": 11,
        "id": "A11-011",
        "source_ids": [
          "A11-011"
        ],
        "anchor": "A11-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "У каждого драйвера есть init, sample, состояние ошибки, ограничение времени и процедура восстановления. Он не блокирует общую систему бесконечным ожиданием байта. Ошибка I²C передаётся как ошибка канала, а не заменяется нулём градусов. После восстановления первый достоверный результат отмечается как новый.",
        "y": 612.4075317382812,
        "page": 39,
        "old": 11,
        "id": "A11-012",
        "source_ids": [
          "A11-012"
        ],
        "anchor": "A11-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для UART-периферии нужен ограниченный буфер, проверка длины и контроль кадра, если он предусмотрен производителем. Неизвестный байт не должен смещать весь поток навсегда. Протокол радара считается внешним недоверенным входом, даже когда модуль физически припаян к плате.",
        "y": 680.2130737304688,
        "page": 39,
        "old": 11,
        "id": "A11-013",
        "source_ids": [
          "A11-013"
        ],
        "anchor": "A11-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Питание и отключаемые домены",
        "y": 56.314456939697266,
        "page": 40,
        "old": 11,
        "id": "A11-014",
        "source_ids": [
          "A11-014"
        ],
        "anchor": "A11-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для стационарного узла простота важнее микроампер сна. Отдельные выключаемые домены полезны как способ восстановить зависший датчик, но добавляют MOSFET/ключ, пусковой ток и риск питания через сигнальные линии. Нельзя отключить VDD сенсора и оставить SDA подтянутой к активной шине, не проверив допустимые режимы его входов.",
        "y": 83.10196685791016,
        "page": 40,
        "old": 11,
        "id": "A11-015",
        "source_ids": [
          "A11-015"
        ],
        "anchor": "A11-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для будущего батарейного устройства отладочная плата может оказаться неподходящей из-за LED, USB-моста и стабилизатора. Тогда выбирается MCU и питание по энергетическому бюджету, а не копируется весь Sense. Общими остаются семантика сообщений и драйверные интерфейсы, но транспорт может стать BLE с пакетной синхронизацией.",
        "y": 150.9075164794922,
        "page": 40,
        "old": 11,
        "id": "A11-016",
        "source_ids": [
          "A11-016"
        ],
        "anchor": "A11-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Что положить на стол",
        "y": 224.34915161132812,
        "page": 40,
        "old": 11,
        "id": "A11-017",
        "source_ids": [
          "A11-017"
        ],
        "anchor": "A11-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Нужны три рабочие платы и одна запасная, кабели с передачей данных, короткие соединения, макетная плата достаточной ширины или переходник, мультиметр и логический анализатор подходящего уровня. При подключении анализатора проверяется допустимое входное напряжение; заземление осциллографа не подключается к неизвестной силовой точке.",
        "y": 251.13670349121094,
        "page": 40,
        "old": 11,
        "id": "A11-018",
        "source_ids": [
          "A11-018"
        ],
        "anchor": "A11-018",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Проверка каждой платы отдельно",
        "y": 351.33062744140625,
        "page": 40,
        "old": 11,
        "id": "A11-019",
        "source_ids": [
          "A11-019"
        ],
        "anchor": "A11-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Записать модель и ревизию, прочитать идентификатор, прошить минимальный диагностический образ, проверить USB, Wi-Fi и свободную память. Затем подключать по одному модулю. Один общий жгут со всеми неизвестными устройствами усложняет поиск ошибки в несколько раз.",
        "y": 369.79180908203125,
        "page": 40,
        "old": 11,
        "id": "A11-020",
        "source_ids": [
          "A11-020"
        ],
        "anchor": "A11-020",
        "content_status": "active"
      },
      {
        "id": "R11-001",
        "kind": "h2",
        "text": "Перед первой подачей питания",
        "source_ids": [
          "R11-001"
        ],
        "anchor": "R11-001",
        "content_status": "active"
      },
      {
        "id": "R11-002",
        "kind": "p",
        "text": "В ведомости фиксируется точный DevKitC-1-N8 и ревизия либо схема выбранного аналога. USB-Serial-JTAG относится к разъёму, соединённому с USB самого C6; второй порт может вести к USB-UART. Названия «USB-C» и «ESP32-C6» не определяют разводку. Strapping-пины, уровни и питание сверяются по документации именно приобретённой платы.",
        "source_ids": [
          "R11-002"
        ],
        "anchor": "R11-002",
        "content_status": "active"
      },
      {
        "id": "R11-003",
        "kind": "p",
        "text": "Первый аппаратный риск — не средний ток датчиков, а просадка питания во время радиопередачи и включения реле. Проверяют напряжение на плате и причину reset при одновременной нагрузке. Большой конденсатор не заменяет достаточный источник, короткие силовые провода и отделение катушки от сенсорной шины.",
        "source_ids": [
          "R11-003"
        ],
        "anchor": "R11-003",
        "content_status": "active"
      }
    ],
    "origins": [
      "A11"
    ],
    "question": "Почему датчик лучше подключать к отдельному узлу?",
    "intro": "Микроконтроллер рядом с датчиком даёт короткие электрические линии и собственный ритм измерений. Он может сохранить несколько записей при обрыве Wi-Fi и не зависит от того, занят ли Linux распознаванием речи.\n\nОтладочная плата избавляет от раннего проектирования питания и радиочасти, но не отменяет чтение её схемы. Надпись на контакте, номер GPIO и физический номер в гребёнке — разные обозначения. Монтажные карты ниже используют все три там, где это необходимо.",
    "id": "ch-11",
    "slug": "chapter-11"
  },
  {
    "number": 12,
    "title": "Температура: что нагрелось на самом деле",
    "group": "Датчики",
    "units": [
      {
        "kind": "lead",
        "text": "После установки в корпус температура чувствительного элемента зависит от всей сборки. Точность измерения температуры комнаты проверяется отдельно от паспортной точности микросхемы.",
        "y": 168.675537109375,
        "page": 41,
        "old": 12,
        "id": "A12-001",
        "source_ids": [
          "A12-001"
        ],
        "anchor": "A12-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первого Sense подходит готовый модуль SHT45. Он одновременно даёт температуру и влажность, а влажность полезна для понимания поведения сенсора и будущих сценариев. Альтернатива — температурный TMP117, когда нужен специализированный термоканал. Более доступный SHT31/SHT4x другого класса может быть достаточен, если его погрешность укладывается в цель опыта; подмена модели должна быть отражена в калибровке и метаданных. [15][78]",
        "y": 219.78143310546875,
        "page": 41,
        "old": 12,
        "id": "A12-002",
        "source_ids": [
          "A12-002"
        ],
        "anchor": "A12-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Микросхема и breakout — разные изделия",
        "y": 331.5256042480469,
        "page": 41,
        "old": 12,
        "id": "A12-003",
        "source_ids": [
          "A12-003"
        ],
        "anchor": "A12-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "У голого SHT45 допустимое питание отличается от удобного входа готовой платы со стабилизатором и дополнительными компонентами. Для рассматриваемого Adafruit-модуля описаны VIN, логические уровни и подтяжки I²C. Подключение VIN к 3,3 В удобно согласует уровни с ESP32, но конкретный неизвестный модуль с маркетплейса требует собственной проверки схемы. [15][16]",
        "y": 358.5432434082031,
        "page": 41,
        "old": 12,
        "id": "A12-004",
        "source_ids": [
          "A12-004"
        ],
        "anchor": "A12-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сначала читаются маркировка и разводка платы, затем выбирается напряжение. Наличие надписи «5V compatible» не означает, что любой контакт GPIO можно соединять с 5 В. Такой подход особенно важен для дешёвых breakout, внешне похожих на оригинал, но содержащих другой стабилизатор или отсутствующий level shifter.",
        "y": 446.92218017578125,
        "page": 41,
        "old": 12,
        "id": "A12-005",
        "source_ids": [
          "A12-005"
        ],
        "anchor": "A12-005",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Где поставить датчик",
        "y": 526.7163696289062,
        "page": 41,
        "old": 12,
        "id": "A12-006",
        "source_ids": [
          "A12-006"
        ],
        "anchor": "A12-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Датчик температуры удаляют от CPU, LDO, силовой катушки и постоянно работающего светодиода. В небольшом корпусе полезен отдельный выступающий участок платы или короткий выносной модуль с доступом воздуха. Измеряемый воздух не должен сначала проходить через горячую электронику.",
        "y": 553.7339477539062,
        "page": 41,
        "old": 12,
        "id": "A12-007",
        "source_ids": [
          "A12-007"
        ],
        "anchor": "A12-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Слишком близкая наружная стена, солнечное пятно и струя кондиционера дают температуру конкретного места, а не условного «среднего кабинета». Это не ошибка сенсора: это ошибка постановки измерения. В реестре нужно хранить положение и назначение канала — например, «воздух у рабочего места», а не обещать температуру всей комнаты.",
        "y": 626.137939453125,
        "page": 41,
        "old": 12,
        "id": "A12-008",
        "source_ids": [
          "A12-008"
        ],
        "anchor": "A12-008",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A13",
        "caption": "Разделение электроники, термоканала и оптического канала. Концепция корпуса должна поддерживать измерительную задачу, а не только защищать плату.",
        "y": 56.69291687011719,
        "page": 42,
        "old": 12,
        "id": "A12-009",
        "source_ids": [
          "A12-009"
        ],
        "anchor": "A12-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Период измерений и тепловая инерция",
        "y": 391.3550720214844,
        "page": 42,
        "old": 12,
        "id": "A12-010",
        "source_ids": [
          "A12-010"
        ],
        "anchor": "A12-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Комната обычно меняется медленнее, чем способен опрашиваться сенсор. Опрос десятки раз в секунду не добавляет полезной информации обычной автоматизации и увеличивает передачу данных. Для первого профиля можно начать с 1 измерения в секунду и публикации при заметном изменении либо периодическом контрольном сообщении. Это проектная настройка, которую уточняют по динамике.",
        "y": 418.37274169921875,
        "page": 42,
        "old": 12,
        "id": "A12-011",
        "source_ids": [
          "A12-011"
        ],
        "anchor": "A12-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "После переноса из одного помещения в другое показание не мгновенно становится температурой нового воздуха. Нужны время установления и отметка переходного режима. Фильтрация уменьшает шум, но добавляет задержку; слишком сильное сглаживание может скрыть реальное изменение.",
        "y": 506.75164794921875,
        "page": 42,
        "old": 12,
        "id": "A12-012",
        "source_ids": [
          "A12-012"
        ],
        "anchor": "A12-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "У сенсоров с нагревателем показания во время его работы не должны использоваться как обычная температура комнаты. В наблюдении ставится флаг режима, а система знает период восстановления. Возможности нагрева и рекомендованное обращение проверяются в документах SHT4x. [15]",
        "y": 579.1555786132812,
        "page": 42,
        "old": 12,
        "id": "A12-013",
        "source_ids": [
          "A12-013"
        ],
        "anchor": "A12-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Калибровка первого экземпляра",
        "y": 658.9497680664062,
        "page": 42,
        "old": 12,
        "id": "A12-014",
        "source_ids": [
          "A12-014"
        ],
        "anchor": "A12-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Проверка включает совместное размещение с опорным прибором, выдержку и сравнение в нескольких устойчивых условиях. Смартфон или произвольный соседний датчик не становится эталоном только потому, что показывает больше знаков. Сохраняются прибор сравнения, дата, размещение, прошивка и исходные выборки.",
        "y": 685.9674072265625,
        "page": 42,
        "old": 12,
        "id": "A12-015",
        "source_ids": [
          "A12-015"
        ],
        "anchor": "A12-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для простого диапазона можно начать со смещения Tcorrected = Traw + b, но его применимость ограничена проверенными условиями. Если нужна линейная коррекция, коэффициенты определяются несколькими точками, а остаточная ошибка показывается отдельно. Автоматически подгонять датчик под ожидаемый комфорт нельзя: это скроет реальные события.",
        "y": 57.42815399169922,
        "page": 43,
        "old": 12,
        "id": "A12-016",
        "source_ids": [
          "A12-016"
        ],
        "anchor": "A12-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Оценка неопределённости складывается не только из паспортной точности: важны опорный прибор, самонагрев, градиент воздуха, задержка и округление. Число «23,437 °C» не полезнее «23,4 °C», если неопределённость значительно больше тысячной доли.",
        "y": 145.80709838867188,
        "page": 43,
        "old": 12,
        "id": "A12-017",
        "source_ids": [
          "A12-017"
        ],
        "anchor": "A12-017",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Сценарии температуры без опасного управления",
        "y": 209.62625122070312,
        "page": 43,
        "old": 12,
        "id": "A12-018",
        "source_ids": [
          "A12-018"
        ],
        "anchor": "A12-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В первом кабинете температура вызывает уведомление или включает низковольтный испытательный вентилятор. Это позволяет изучить обратную связь без автоматического управления обогревателем. Если вентилятор просто перемешивает воздух, он не обязан снижать среднюю температуру помещения: ощущение человека и тепловой баланс — разные величины.",
        "y": 236.64389038085938,
        "page": 43,
        "old": 12,
        "id": "A12-019",
        "source_ids": [
          "A12-019"
        ],
        "anchor": "A12-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для будущей тепловой автоматики понадобятся ограничения времени, независимая защита, оценка наружных условий и проверка исполнительного оборудования. Нельзя перенести правило рабочего света на нагреватель, изменив только имя канала.",
        "y": 325.0228576660156,
        "page": 43,
        "old": 12,
        "id": "A12-020",
        "source_ids": [
          "A12-020"
        ],
        "anchor": "A12-020",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Эксперимент с самонагревом",
        "y": 397.7216491699219,
        "page": 43,
        "old": 12,
        "id": "A12-021",
        "source_ids": [
          "A12-021"
        ],
        "anchor": "A12-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Снять ряд показаний при выключенном и активно передающем Wi-Fi, не меняя внешнего положения узла. Повторить с открытым и закрытым корпусом. Если смещение связано с режимом платы, исправлять размещение и тепловую конструкцию, а не только добавлять постоянную поправку в код.",
        "y": 416.182861328125,
        "page": 43,
        "old": 12,
        "id": "A12-022",
        "source_ids": [
          "A12-022"
        ],
        "anchor": "A12-022",
        "content_status": "active"
      },
      {
        "id": "R12-001",
        "kind": "h2",
        "text": "Преобразование АЦП и реакция на воздух — разные времена",
        "source_ids": [
          "R12-001"
        ],
        "anchor": "R12-001",
        "content_status": "active"
      },
      {
        "id": "R12-002",
        "kind": "p",
        "text": "SHT4x описывает режимы преобразования, CRC и время готовности. Для высокоточного чтения драйвер использует предельное время из актуального даташита; учебная программная граница ожидания 10 мс не выдаётся за точное время физического интегрирования. Температурная реакция корпуса сенсора на воздух медленнее преобразования. В Observation записывается интервал, который действительно ограничивает драйвер, и режим получения данных. [157]",
        "source_ids": [
          "R12-002"
        ],
        "anchor": "R12-002",
        "content_status": "active"
      },
      {
        "id": "R12-003",
        "kind": "p",
        "text": "Узел хранит исходные температурные и влажностные отсчёты, статус нагревателя и версию калибровки. Самонагрев влияет и на относительную влажность: более тёплый чувствительный элемент при том же содержании водяного пара показывает меньшую RH. Проверка сборки сравнивает близко расположенный эталон после установления, а не пытается корректировать всю комнату по одной поправке.",
        "source_ids": [
          "R12-003"
        ],
        "anchor": "R12-003",
        "content_status": "active"
      }
    ],
    "origins": [
      "A12"
    ],
    "question": "Измеряем воздух комнаты или нагрев своей электроники?",
    "intro": "Температурный сенсор измеряет условия у самого чувствительного элемента. Если рядом горячий стабилизатор, корпус удерживает тепло или на плату падает солнце, точность микросхемы не спасает результат.\n\nПоэтому первым опытом будет не сравнение десятых долей градуса в приложении, а наблюдение реакции на размещение: внутри и вне корпуса, возле стены и на открытом воздухе комнаты, при работающем и выключенном радио. Так появляется понимание измерительной системы целиком.",
    "id": "ch-12",
    "slug": "chapter-12"
  },
  {
    "number": 13,
    "title": "Свет: число в правильной точке",
    "group": "Датчики",
    "units": [
      {
        "kind": "lead",
        "text": "Люксометр видит свет в своей точке. Чтобы управлять комнатой, нужно понимать, откуда этот свет пришёл.",
        "y": 168.675537109375,
        "page": 44,
        "old": 13,
        "id": "A13-001",
        "source_ids": [
          "A13-001"
        ],
        "anchor": "A13-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "VEML7700 подходит для первого оптического канала: готовый модуль легко подключить к I²C, а усиление и время интегрирования позволяют исследовать разные уровни света. OPT3001 — альтернативный вариант с другой внутренней организацией и драйвером. Выбор между ними делается по диапазону, спектральной характеристике, доступности модуля и поведению при насыщении, а не по одному числу разрядности. [17] [18][19]",
        "y": 219.78143310546875,
        "page": 44,
        "old": 13,
        "id": "A13-002",
        "source_ids": [
          "A13-002"
        ],
        "anchor": "A13-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Подключение и адреса",
        "y": 331.5256042480469,
        "page": 44,
        "old": 13,
        "id": "A13-003",
        "source_ids": [
          "A13-003"
        ],
        "anchor": "A13-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В выбранной паре VEML7700 использует семибитный адрес 0x10, SHT4x — 0x44. Они могут находиться на одной шине. Формат адреса важен: некоторые библиотеки ожидают семибитное значение, другие примеры показывают уже сдвинутый байт с битом чтения/записи. Передача 0x20 вместо 0x10 в неподходящий API даст отсутствие ответа, а не неисправность датчика. [17][16]",
        "y": 358.5432434082031,
        "page": 44,
        "old": 13,
        "id": "A13-004",
        "source_ids": [
          "A13-004"
        ],
        "anchor": "A13-004",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A14",
        "caption": "Общая I²C-шина двух модулей. Показаны эквивалентные подтяжки: реальные резисторы могут уже находиться на breakout-платах.",
        "y": 454.6908874511719,
        "page": 44,
        "old": 13,
        "id": "A13-005",
        "source_ids": [
          "A13-005"
        ],
        "anchor": "A13-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "SDA и SCL имеют открытый сток: высокий уровень формируется подтяжкой. Слишком большое сопротивление делает фронт медленным; слишком маленькое увеличивает ток в состоянии логического нуля. Общая ёмкость состоит из выводов, проводов, разъёмов и входов анализатора. Поэтому правило «всегда ставить 4,7 кΩ» недостаточно.",
        "y": 57.42815399169922,
        "page": 45,
        "old": 13,
        "id": "A13-006",
        "source_ids": [
          "A13-006"
        ],
        "anchor": "A13-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для порогов 30% и 70% напряжения питания спецификация I²C даёт зависимость tᵣ = 0,8473 · Rₚ · Cᵦ. Минимальное сопротивление ограничивается допустимым током стягивания и уровнем VOL. [26]",
        "y": 129.83209228515625,
        "page": 45,
        "old": 13,
        "id": "A13-007",
        "source_ids": [
          "A13-007"
        ],
        "anchor": "A13-007",
        "content_status": "active"
      },
      {
        "kind": "quote",
        "text": "При Cᵦ = 100 пФ и Rₚ = 4,7 кΩ: tᵣ ≈ 398 нс. При той же ёмкости и Rₚ = 2,2 кΩ: tᵣ ≈ 186 нс.",
        "y": 204.84298706054688,
        "page": 45,
        "old": 13,
        "id": "A13-008",
        "source_ids": [
          "A13-008"
        ],
        "anchor": "A13-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первое значение укладывается в типичный предел Standard-mode 100 кГц, но не в предел 300 нс для Fast-mode. Второе быстрее, однако ток и допустимые уровни проверяются для всех участников. Две подтяжки по 10 кΩ, соединённые параллельно, дают 5 кΩ. Добавление третьего модуля снова меняет эквивалентное сопротивление. [26]",
        "y": 266.247314453125,
        "page": 45,
        "old": 13,
        "id": "A13-009",
        "source_ids": [
          "A13-009"
        ],
        "anchor": "A13-009",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A15",
        "caption": "Форма нарастающего фронта определяется R·C. Иллюстрация не является осциллограммой собранного узла.",
        "y": 346.419921875,
        "page": 45,
        "old": 13,
        "id": "A13-010",
        "source_ids": [
          "A13-010"
        ],
        "anchor": "A13-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первого стенда предпочтительны короткие провода и 100 кГц. Длина порядка 10– 20 см — удобное исходное ограничение макета, а не физический предел стандарта. Длинный кабель через комнату лучше обслуживать отдельным узлом или подходящим дифференциальным интерфейсом, чем пытаться сделать I²C универсальной полевой сетью.",
        "y": 626.1328125,
        "page": 45,
        "old": 13,
        "id": "A13-011",
        "source_ids": [
          "A13-011"
        ],
        "anchor": "A13-011",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Усиление, интегрирование и насыщение",
        "y": 56.314456939697266,
        "page": 46,
        "old": 13,
        "id": "A13-012",
        "source_ids": [
          "A13-012"
        ],
        "anchor": "A13-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Датчик накапливает сигнал за интервал. Увеличение времени интегрирования повышает чувствительность к слабому свету, но снижает скорость обновления и раньше приводит к насыщению при ярком освещении. Автодиапазон меняет настройку, затем ждёт корректного нового измерения; нельзя применять новый коэффициент к результату, накопленному со старым режимом. VEML7700 имеет документированные gain и integration time. [17]",
        "y": 83.33208465576172,
        "page": 46,
        "old": 13,
        "id": "A13-013",
        "source_ids": [
          "A13-013"
        ],
        "anchor": "A13-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Значение у верхней границы ADC сопровождается флагом насыщения. Оно не превращается в «очень точно измеренные 65535». В переходном режиме после смены настройки публикация может быть временно задержана или помечена. Время измерения относится к интервалу интегрирования, а не только к моменту чтения регистра.",
        "y": 187.68603515625,
        "page": 46,
        "old": 13,
        "id": "A13-014",
        "source_ids": [
          "A13-014"
        ],
        "anchor": "A13-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Оптика корпуса",
        "y": 267.4801940917969,
        "page": 46,
        "old": 13,
        "id": "A13-015",
        "source_ids": [
          "A13-015"
        ],
        "anchor": "A13-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Прозрачная крышка не нейтральна по определению. Тонировка, матовая поверхность, геометрия канала и угол света изменяют измерение. Датчик должен смотреть на полезную плоскость или на репрезентативную область, а не на собственный индикатор. Если нужен комфорт на столе, вертикальная стенка у двери может давать плохо связанную величину.",
        "y": 294.4978332519531,
        "page": 46,
        "old": 13,
        "id": "A13-016",
        "source_ids": [
          "A13-016"
        ],
        "anchor": "A13-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Нельзя установить люксометр глубоко за узким отверстием и затем ожидать прежней угловой характеристики. Для первого корпуса лучше широкое проверяемое окно, затем сравнение с открытым модулем. Параметры окна входят в аппаратную ревизию и калибровку.",
        "y": 382.87677001953125,
        "page": 46,
        "old": 13,
        "id": "A13-017",
        "source_ids": [
          "A13-017"
        ],
        "anchor": "A13-017",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Свет, который система включила сама",
        "y": 462.6709289550781,
        "page": 46,
        "old": 13,
        "id": "A13-018",
        "source_ids": [
          "A13-018"
        ],
        "anchor": "A13-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Простое правило «ниже 80 лк — включить, выше 120 лк — выключить» часто образует цикл: лампа включилась, датчик увидел 500 лк, правило выключило лампу, снова стало темно. Один гистерезис не решает проблему, если собственная лампа меняет измерение сильнее ширины гистерезиса.",
        "y": 489.6885681152344,
        "page": 46,
        "old": 13,
        "id": "A13-019",
        "source_ids": [
          "A13-019"
        ],
        "anchor": "A13-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В E0 с PIR-only включение использует допустимое событие движения и освещённость до включения; автоматический OFF по отсутствию движения не разрешён. Выключение после подтверждённого ухода с выдержкой относится к отдельно квалифицированному профилю E1. Для выключения при достаточном дневном свете требуется отдельный дневной канал или модель собственного вклада светильника с проверкой применимости. Яркость от включённой лампы сама по себе не является основанием для выключения.",
        "y": 562.092529296875,
        "page": 46,
        "old": 13,
        "id": "A13-020",
        "source_ids": [
          "A13-020"
        ],
        "anchor": "A13-020",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A16",
        "caption": "Самовозбуждение световой автоматизации. Красная ступенчатая линия — объясняющий пример, не результат измерения.",
        "y": 56.69291687011719,
        "page": 47,
        "old": 13,
        "id": "A13-021",
        "source_ids": [
          "A13-021"
        ],
        "anchor": "A13-021",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Проверка канала",
        "y": 391.3550720214844,
        "page": 47,
        "old": 13,
        "id": "A13-022",
        "source_ids": [
          "A13-022"
        ],
        "anchor": "A13-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Проводятся опыты: затемнение, обычный рабочий свет, дневной свет, направленная лампа, закрытый корпус, смена gain. В каждом сохраняются raw, настройки, рассчитанные lux и флаги. Мерцание источника и длительность интегрирования могут влиять на разброс; среднее число не описывает качество мерцания или безопасность освещения.",
        "y": 418.37274169921875,
        "page": 47,
        "old": 13,
        "id": "A13-023",
        "source_ids": [
          "A13-023"
        ],
        "anchor": "A13-023",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Полезный результат главы",
        "y": 523.0215454101562,
        "page": 47,
        "old": 13,
        "id": "A13-024",
        "source_ids": [
          "A13-024"
        ],
        "anchor": "A13-024",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Получить таблицу «сырое значение → настройки → lux → качество», устойчивое чтение двух датчиков на одной шине и осциллограмму фронтов. Затем проверить, что собственное включение лампы не приводит к бесконечному ON/OFF.",
        "y": 541.4827270507812,
        "page": 47,
        "old": 13,
        "id": "A13-025",
        "source_ids": [
          "A13-025"
        ],
        "anchor": "A13-025",
        "content_status": "active"
      },
      {
        "id": "R13-001",
        "kind": "h2",
        "text": "Коррекция света должна быть воспроизводимой",
        "source_ids": [
          "R13-001"
        ],
        "anchor": "R13-001",
        "content_status": "active"
      },
      {
        "id": "R13-002",
        "kind": "p",
        "text": "В примечании Vishay 84323 описаны gain, integration time и коррекция нелинейности. Нельзя применять один коэффициент для всех диапазонов или полином дважды — в драйвере и Core. Сохраняются raw counts, gain, IT, признак насыщения и версия преобразования. При смене диапазона первый непригодный результат исключается. Для gain 1/4 и 1/8 документ приводит полином; в рабочем профиле порог и коэффициенты берутся из закреплённой редакции и проверяются на контрольных точках. [106]",
        "source_ids": [
          "R13-002"
        ],
        "anchor": "R13-002",
        "content_status": "active"
      },
      {
        "id": "R13-003",
        "kind": "p",
        "text": "Интегрирование 100 мс удобно для первого сетевого освещения: оно усредняет несколько периодов типичной пульсации, но не устраняет любой PWM-драйвер и не создаёт точной синхронизации с сетью. 25 мс и 50 мс не следует объявлять универсальной защитой от фликера. При сильной пульсации сравниваются несколько IT на одной лампе и сохраняется разброс.",
        "source_ids": [
          "R13-003"
        ],
        "anchor": "R13-003",
        "content_status": "active"
      },
      {
        "id": "R13-004",
        "kind": "table",
        "rows": [
          [
            "Канал / альтернатива",
            "Адрес I²C",
            "Что проверить"
          ],
          [
            "SHT45-AD1B",
            "0x44",
            "Не путать с другими артикулами SHT4x"
          ],
          [
            "VEML7700",
            "0x10",
            "Подтяжки уже могут быть на breakout"
          ],
          [
            "OPT3001",
            "0x44…0x47 по ADDR",
            "При SHT45 выбрать другой адрес/шину"
          ],
          [
            "INA226",
            "Зависит от A0/A1",
            "Записать перемычки конкретной платы"
          ]
        ],
        "source_ids": [
          "R13-004"
        ],
        "anchor": "R13-004",
        "content_status": "active"
      },
      {
        "id": "R13-005",
        "kind": "p",
        "text": "Два набора подтяжек по 10 кОм дают около 5 кОм; добавление ещё одной пары вслепую меняет нагрузку на выходы. Проверяют итоговое сопротивление и фронты собранной шины, а не только перечень модулей. [158]",
        "source_ids": [
          "R13-005"
        ],
        "anchor": "R13-005",
        "content_status": "active"
      }
    ],
    "origins": [
      "A13"
    ],
    "question": "Как не перепутать свет в комнате со светом на датчике?",
    "intro": "Люксометр у монитора и люксометр на потолке отвечают на разные вопросы. Для первого сценария выбираем одну рабочую плоскость и фиксируем ориентацию сенсора. Тогда фраза «стало светлее» относится к воспроизводимому месту, а не ко всей комнате сразу.\n\nЗдесь же разберём I²C. Это короткая электрическая шина, а не комнатная сеть. Её подтяжки и ёмкость влияют на фронты сигнала; правильный адрес устройства ещё не доказывает, что соединение устойчиво.",
    "id": "ch-13",
    "slug": "chapter-13"
  },
  {
    "number": 14,
    "title": "Присутствие без иллюзии всеведения",
    "group": "Датчики",
    "units": [
      {
        "kind": "lead",
        "text": "Движение, присутствие и личность — разные задачи. Первый Koobique решает только первые две.",
        "y": 168.675537109375,
        "page": 48,
        "old": 14,
        "id": "A14-001",
        "source_ids": [
          "A14-001"
        ],
        "anchor": "A14-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "PIR обнаруживает изменения теплового излучения в своей зоне, поэтому хорошо подходит для входа и движения. Неподвижный человек может перестать давать события. Радар способен обнаруживать другую динамику, но также реагировать на вентилятор, шторы, соседнее помещение или собственные особенности установки. Ни один из этих сигналов не следует превращать в абсолютную истину без проверки. [20][22]",
        "y": 219.5513153076172,
        "page": 48,
        "old": 14,
        "id": "A14-002",
        "source_ids": [
          "A14-002"
        ],
        "anchor": "A14-002",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A17",
        "caption": "Испытательное размещение в кабинете. Отдельный узел присутствия позволяет выбрать полезный обзор, не подчиняя его положению вычислительного куба.",
        "y": 311.2440490722656,
        "page": 48,
        "old": 14,
        "id": "A14-003",
        "source_ids": [
          "A14-003"
        ],
        "anchor": "A14-003",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Три варианта узла Presence",
        "y": 693.6384887695312,
        "page": 48,
        "old": 14,
        "id": "A14-004",
        "source_ids": [
          "A14-004"
        ],
        "anchor": "A14-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "PIR-only — минимальный стенд обнаружения движения: он допускает включение по событию входа, но не вывод «человек ушёл». В первом профиле автоматическое выключение по одному молчанию PIR запрещено. Таймер коридора можно добавить как отдельную явно выбранную политику, а не назвать измерением отсутствия.",
        "y": 720.426025390625,
        "page": 48,
        "old": 14,
        "id": "A14-005",
        "source_ids": [
          "A14-005"
        ],
        "anchor": "A14-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "PIR + радар — предпочтительный исследовательский комплект. PIR помогает быстрому событию входа, радар поддерживает оценку присутствия во время спокойной работы. Параметры дальности, чувствительности и периода выдачи регулируются на основании журнала ошибок.",
        "y": 57.198036193847656,
        "page": 49,
        "old": 14,
        "id": "A14-006",
        "source_ids": [
          "A14-006"
        ],
        "anchor": "A14-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Дополнительный контекст — геркон двери, кнопка, состояние рабочего компьютера или явно включённый режим работы. Эти каналы помогают, но не должны скрыто собирать личные данные. Активность телефона и RSSI не считаются доказательством нахождения конкретного человека в комнате.",
        "y": 125.00357818603516,
        "page": 49,
        "old": 14,
        "id": "A14-007",
        "source_ids": [
          "A14-007"
        ],
        "anchor": "A14-007",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Выбор готового радара",
        "y": 198.44522094726562,
        "page": 49,
        "old": 14,
        "id": "A14-008",
        "source_ids": [
          "A14-008"
        ],
        "anchor": "A14-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "SEN0395 — пример модуля с опубликованной документацией и интерфейсом. Его полезность для первого проекта — в доступном протоколе и понятной интеграции, а не в гарантии отсутствия ложных срабатываний. Перед подключением сверяются питание, допустимые уровни UART, назначение выводов и режим данных конкретной ревизии. Не каждый модуль, питающийся от 5 В, выдаёт безопасный для ESP32 логический уровень. [22][23]",
        "y": 225.23275756835938,
        "page": 49,
        "old": 14,
        "id": "A14-009",
        "source_ids": [
          "A14-009"
        ],
        "anchor": "A14-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Более дешёвые радары допустимы, если есть читаемая документация и воспроизводимая настройка. При отсутствии протокола приходится начинать с изучения модуля, что может стоить больше времени, чем экономия при покупке. Сырой поток радара не обязательно нужен ядру: часто достаточно локально выделенных признаков и диагностического режима по запросу.",
        "y": 308.0762634277344,
        "page": 49,
        "old": 14,
        "id": "A14-010",
        "source_ids": [
          "A14-010"
        ],
        "anchor": "A14-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Размещение в комнате",
        "y": 396.5559387207031,
        "page": 49,
        "old": 14,
        "id": "A14-011",
        "source_ids": [
          "A14-011"
        ],
        "anchor": "A14-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "PIR направляется через предполагаемую траекторию движения, а не только вдоль неё. Радар не ориентируется на вращающийся вентилятор или движущуюся штору. Металлические детали корпуса и стены влияют на поле; пластиковая крышка также проверяется. Сенсор присутствия не обязан находиться в одном корпусе со станцией.",
        "y": 423.3434143066406,
        "page": 49,
        "old": 14,
        "id": "A14-012",
        "source_ids": [
          "A14-012"
        ],
        "anchor": "A14-012",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Как собирать данные для настройки",
        "y": 496.78509521484375,
        "page": 49,
        "old": 14,
        "id": "A14-013",
        "source_ids": [
          "A14-013"
        ],
        "anchor": "A14-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Нужны размеченные периоды: пустая комната, вход, сидение без движения, работа за компьютером, движения за стеной, работа вентилятора, открытое окно, уход. Разметка может выполняться кнопкой в приложении: «сейчас пусто» / «сейчас я здесь». Записываются настройки радара и версия алгоритма.",
        "y": 523.5726318359375,
        "page": 49,
        "old": 14,
        "id": "A14-014",
        "source_ids": [
          "A14-014"
        ],
        "anchor": "A14-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Основные метрики — пропущенное присутствие, ложное присутствие, время включения после входа и время выключения после ухода. Средняя точность по всем секундам может вводить в заблуждение, если комната большую часть времени пуста. Отдельно считается нежелательное выключение света при человеке.",
        "y": 591.3782348632812,
        "page": 49,
        "old": 14,
        "id": "A14-015",
        "source_ids": [
          "A14-015"
        ],
        "anchor": "A14-015",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Модель состояний",
        "y": 664.81982421875,
        "page": 49,
        "old": 14,
        "id": "A14-016",
        "source_ids": [
          "A14-016"
        ],
        "anchor": "A14-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первого экземпляра достаточно явного автомата: UNKNOWN, EMPTY, CANDIDATE, OCCUPIED и VACATING. UNKNOWN означает отсутствие пригодной свежей информации. CANDIDATE — краткий период проверки сигнала. VACATING — отсутствие новых свидетельств, но ещё не подтверждённый уход.",
        "y": 691.6073608398438,
        "page": 49,
        "old": 14,
        "id": "A14-017",
        "source_ids": [
          "A14-017"
        ],
        "anchor": "A14-017",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A18",
        "caption": "Два слоя присутствия: оценка состояния и обязательная проверка качества. UNKNOWN не означает EMPTY; PIR-only не разрешает автоматический уход.",
        "y": 56.69291687011719,
        "page": 50,
        "old": 14,
        "id": "A14-018",
        "source_ids": [
          "A14-018"
        ],
        "anchor": "A14-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "До калибровки вероятностной модели используем категорию уверенности и перечень свидетельств: «радар активен; PIR сработал 12 секунд назад; данные свежие». Число вроде «вероятность 0,93» требует размеченных записей и проверки калибровки; без них точная цифра создаёт необоснованное доверие.",
        "y": 434.6481018066406,
        "page": 50,
        "old": 14,
        "id": "A14-019",
        "source_ids": [
          "A14-019"
        ],
        "anchor": "A14-019",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Выдержки и дребезг",
        "y": 508.0897521972656,
        "page": 50,
        "old": 14,
        "id": "A14-020",
        "source_ids": [
          "A14-020"
        ],
        "anchor": "A14-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Короткие импульсы объединяются локально, если это соответствует характеристикам сенсора. Станция отдельно хранит время последнего валидного события. Пропавшее сообщение не обнуляет занятость немедленно. История, досланная после восстановления связи, не должна продлевать текущее присутствие как новое событие.",
        "y": 534.8773193359375,
        "page": 50,
        "old": 14,
        "id": "A14-021",
        "source_ids": [
          "A14-021"
        ],
        "anchor": "A14-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для проверенного комплекта PIR + радар можно исследовать быстрое подтверждение входа и задержку ухода. Для PIR-only правило выключения не включается. Порог 120 секунд в примерах ниже относится к гипотезе проверяемого профиля, а не к паспортной возможности PIR. При UNKNOWN автоматизация не делает вид, что комната пуста.",
        "y": 602.682861328125,
        "page": 50,
        "old": 14,
        "id": "A14-022",
        "source_ids": [
          "A14-022"
        ],
        "anchor": "A14-022",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Проверка, важнее любого демо",
        "y": 687.8387451171875,
        "page": 50,
        "old": 14,
        "id": "A14-023",
        "source_ids": [
          "A14-023"
        ],
        "anchor": "A14-023",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для Presence-qualified сесть неподвижно на 30 минут, затем оставить пустую комнату с вентилятором. Самопроизвольное OFF при человеке или неспособность подтвердить уход означают, что допуск профиля не получен. PIR-only в этом опыте не обязан доказать пустоту; его критерий — не выключать свет по одному отсутствию движений.",
        "y": 706.2999267578125,
        "page": 50,
        "old": 14,
        "id": "A14-024",
        "source_ids": [
          "A14-024"
        ],
        "anchor": "A14-024",
        "content_status": "active"
      },
      {
        "id": "R14-001",
        "kind": "h2",
        "text": "Два допуска вместо одного слова presence",
        "source_ids": [
          "R14-001"
        ],
        "anchor": "R14-001",
        "content_status": "active"
      },
      {
        "id": "R14-002",
        "kind": "table",
        "rows": [
          [
            "Профиль",
            "Разрешено",
            "Не выводится из данных"
          ],
          [
            "PIR-only / E0",
            "Событие входа и включение при темноте; ручное OFF",
            "Отсутствие человека по тишине PIR"
          ],
          [
            "Presence-qualified / E1",
            "Автомат ухода после размеченной проверки набора признаков",
            "Безошибочность радара либо здоровье канала из boolean"
          ],
          [
            "UNKNOWN",
            "Остановить новые автоматические выводы; уведомить",
            "EMPTY и разрешение выключать по факту потери связи"
          ]
        ],
        "source_ids": [
          "R14-002"
        ],
        "anchor": "R14-002",
        "content_status": "active"
      },
      {
        "id": "R14-003",
        "kind": "p",
        "text": "Автомат качества действует поверх всех состояний: из EMPTY, CANDIDATE, OCCUPIED и VACATING потеря требуемого наблюдения ведёт в UNKNOWN. После восстановления снова проверяются свидетельства: положительное ведёт к CANDIDATE/OCCUPIED, а EMPTY возможно только при допущенном профиле отсутствия. CANDIDATE возвращается в EMPTY при опровергнутом входе лишь там, где отсутствие наблюдаемо.",
        "source_ids": [
          "R14-003"
        ],
        "anchor": "R14-003",
        "content_status": "active"
      },
      {
        "id": "R14-004",
        "kind": "p",
        "text": "В E0 отсутствие данных не гасит свет автоматически. Через 5 минут UNKNOWN выводится уведомление «датчик недоступен; свет под ручным управлением». Это предел безмолвного ожидания, не предел включённого света. Автоматический тайм-аут отключения — отдельная настройка с предупреждением; по умолчанию он выключен. Так неисправный PIR не превращается в ложное доказательство пустой комнаты.",
        "source_ids": [
          "R14-004"
        ],
        "anchor": "R14-004",
        "content_status": "active"
      },
      {
        "id": "R14-005",
        "kind": "p",
        "text": "Выбранный PIR в паспорте имеет модель, время прогрева, retrigger, длину импульса и мёртвое время. До окончания прогрева канал не объявляется пригодным. Проверяют неподвижную работу 30 минут, пустое помещение с вентилятором, затенение обзора и радиопередачу рядом. Таймер 120 секунд не исправляет фундаментальную ненаблюдаемость PIR-only.",
        "source_ids": [
          "R14-005"
        ],
        "anchor": "R14-005",
        "content_status": "active"
      },
      {
        "id": "R3-14-HEALTH-H",
        "kind": "h2",
        "text": "Качество события и смена размещения",
        "source_ids": [],
        "anchor": "R3-14-HEALTH-H",
        "content_status": "r3_addition"
      },
      {
        "id": "R3-14-HEALTH-P",
        "kind": "p",
        "text": "Событие движения должно быть связано с тем размещением и режимом сенсора, в которых оно возникло. Смена descriptor без перезапуска не делает старое событие новым. До заморозки профиля Event нужно определить передачу descriptor_revision, placement_revision и mode_id либо однозначную границу sample_seq для каждого описания. Динамическое здоровье канала требует отдельного статуса с временем и причиной: WARMING, VALID или INVALID. Живой сетевой Ping сам по себе не доказывает исправность PIR. Это открытое требование контракта, а не утверждение о реализованном сообщении NodeHealth.",
        "source_ids": [],
        "anchor": "R3-14-HEALTH-P",
        "content_status": "r3_addition"
      }
    ],
    "origins": [
      "A14"
    ],
    "question": "Как отличить тишину датчика от пустой комнаты?",
    "intro": "Человек, который читает за столом, почти не двигается. Для PIR это может выглядеть так же, как отсутствие человека. Радар добавляет свидетельства, но тоже видит мир через свою диаграмму направленности, настройки и помехи.\n\nПоэтому состояние комнаты собирается из нескольких признаков и времени. Полезные ответы — «занята», «вероятно свободна» и «не хватает данных». Состояние «неизвестно» не следует автоматически превращать в команду выключения света.",
    "id": "ch-14",
    "slug": "chapter-14"
  },
  {
    "number": 15,
    "title": "От команды к замыканию контакта",
    "group": "Исполнение",
    "units": [
      {
        "kind": "lead",
        "text": "Проектирование выхода начинается с нагрузки, допустимых состояний и отказов. Из этих условий выводится контракт команды, включая ограничения на применение `toggle()`.",
        "y": 168.675537109375,
        "page": 51,
        "old": 15,
        "id": "A15-001",
        "source_ids": [
          "A15-001"
        ],
        "anchor": "A15-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первый Actuator переключает готовую 12-вольтовую лампу. На столе используются ESP32-C6, готовый релейный модуль с документированным управляющим входом и отдельный источник нагрузки. После проверки можно заменить модуль собственной схемой драйвера или MOSFET-ключом — это разные решения с разными свойствами.",
        "y": 202.38140869140625,
        "page": 51,
        "old": 15,
        "id": "A15-002",
        "source_ids": [
          "A15-002"
        ],
        "anchor": "A15-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Реле или полупроводник",
        "y": 282.17559814453125,
        "page": 51,
        "old": 15,
        "id": "A15-003",
        "source_ids": [
          "A15-003"
        ],
        "anchor": "A15-003",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Решение",
            "Плюсы",
            "Ограничения",
            "Подходящий первый опыт"
          ],
          [
            "Механическое реле",
            "Сухие контакты; понятное размыкание цепи",
            "Щелчок, ресурс контактов, дуга, ток катушки",
            "Включение/выключение лампы"
          ],
          [
            "MOSFET",
            "Быстрое переключение; PWM для совместимой нагрузки",
            "Нет механического разрыва; потери и выбросы",
            "Низковольтная лента, вентилятор"
          ],
          [
            "Готовый SSR",
            "Удобный модуль под определённую нагрузку",
            "Ток утечки, нагрев; AC и DC не взаимозаменяемы",
            "Только после проверки точного типа"
          ],
          [
            "Бистабильное реле",
            "Не требует постоянного тока удержания",
            "Сложнее знать физическое состояние после сбоя",
            "Поздний энергосберегающий узел"
          ]
        ],
        "y": 311.29,
        "page": 51,
        "old": 15,
        "id": "A15-004",
        "source_ids": [
          "A15-004"
        ],
        "anchor": "A15-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Маркировка тока контактов для AC не переносится автоматически на DC. Светодиодный драйвер может иметь пусковой ток значительно выше установившегося. Поэтому выбранный модуль проверяется по типу нагрузки, а не по самой крупной цифре на корпусе.",
        "y": 577.3406372070312,
        "page": 51,
        "old": 15,
        "id": "A15-005",
        "source_ids": [
          "A15-005"
        ],
        "anchor": "A15-005",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Готовый релейный модуль",
        "y": 657.1348266601562,
        "page": 51,
        "old": 15,
        "id": "A15-006",
        "source_ids": [
          "A15-006"
        ],
        "anchor": "A15-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "STEMMA Non-Latching Mini Relay — пример документированного модуля с входом управления, питанием и контактами. Он позволяет проверить архитектуру без немедленного монтажа транзистора и защитного диода. У неизвестной платы с надписью «opto isolated» нужно изучить реальные связи земли и перемычки: наличие оптопары ещё не гарантирует гальваническую развязку всей сборки. [24][25]",
        "y": 684.1524047851562,
        "page": 51,
        "old": 15,
        "id": "A15-007",
        "source_ids": [
          "A15-007"
        ],
        "anchor": "A15-007",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A19",
        "caption": "Первый низковольтный исполнитель. Реле переключает плюс лампы; цепь лампы и питание логики показаны раздельно.",
        "y": 56.69291687011719,
        "page": 52,
        "old": 15,
        "id": "A15-008",
        "source_ids": [
          "A15-008"
        ],
        "anchor": "A15-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "COM соединяется с защищённым плюсом источника 12 В DC, NO — с плюсом лампы, минус лампы — с минусом источника. NC не используется. В E0 допускается только документированный активный HIGH-модуль, совместимый с 3,3 В. Неизвестный 5-вольтовый active-LOW-модуль запрещён до проверки схемы: его подтяжка может подать 5 В на GPIO, а уровень 3,3 В не гарантирует выключение.",
        "y": 394.95697021484375,
        "page": 52,
        "old": 15,
        "id": "A15-009",
        "source_ids": [
          "A15-009"
        ],
        "anchor": "A15-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Собственный драйвер катушки",
        "y": 474.7511291503906,
        "page": 52,
        "old": 15,
        "id": "A15-010",
        "source_ids": [
          "A15-010"
        ],
        "anchor": "A15-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для малой 5-вольтовой катушки можно исследовать низкобоковой N-MOSFET. Исток соединён с GND, сток — с нижним выводом катушки, верхний вывод — с +5 В. Диод параллельно катушке установлен катодом к плюсу. Резистор затвора ограничивает резкие токи переключения; pull-down удерживает выключение при неопределённом GPIO.",
        "y": 501.76873779296875,
        "page": 52,
        "old": 15,
        "id": "A15-011",
        "source_ids": [
          "A15-011"
        ],
        "anchor": "A15-011",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A20",
        "caption": "Схема для изучения собственного драйвера. Номиналы R1/R2 примерные; диод, ток катушки, разводка и режимы Q1 выбираются и проверяются отдельно.",
        "y": 56.69291687011719,
        "page": 53,
        "old": 15,
        "id": "A15-012",
        "source_ids": [
          "A15-012"
        ],
        "anchor": "A15-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "AO3400A интересен тем, что его паспорт нормирует сопротивление открытого канала и при VGS = 2,5 В: это существенно полезнее для 3,3-вольтового MCU, чем одно пороговое напряжение VGS(th). Порог означает начало проводимости при малом токе, а не гарантированное открывание силового ключа. [80]",
        "y": 453.5081787109375,
        "page": 53,
        "old": 15,
        "id": "A15-013",
        "source_ids": [
          "A15-013"
        ],
        "anchor": "A15-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "При условном токе катушки 80 мА и сопротивлении канала 0,048 Ω проводниковые потери составляют около 0,00031 Вт. Этот расчёт показывает, что для такой катушки тепловая проблема ключа мала; он не разрешает применять тот же корпус к многоамперной нагрузке. Сопротивление растёт с температурой, а импульсные режимы и плата влияют на допустимый ток.",
        "y": 525.912109375,
        "page": 53,
        "old": 15,
        "id": "A15-014",
        "source_ids": [
          "A15-014"
        ],
        "anchor": "A15-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Обычный обратный диод замедляет спад тока катушки. Если требуется быстрое отпускание, исследуют ограничение выброса TVS/стабилитроном и проверяют допустимое напряжение ключа. Это изменение влияет на механику контактов и не вводится «для ускорения» без измерения.",
        "y": 614.291015625,
        "page": 53,
        "old": 15,
        "id": "A15-015",
        "source_ids": [
          "A15-015"
        ],
        "anchor": "A15-015",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Ток как обратная связь",
        "y": 56.314456939697266,
        "page": 54,
        "old": 15,
        "id": "A15-016",
        "source_ids": [
          "A15-016"
        ],
        "anchor": "A15-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "INA226 измеряет ток через шунт; диапазон его шунтового входа составляет около ±81,92 мВ. При 0,1 Ом и 0,5 А получаются 50 мВ и 25 мВт. При 2 А — уже 200 мВ: АЦП насыщен, даже если резистор выдерживает 0,4 Вт. Для учебного диапазона до 2 А шунт 0,02 Ом даёт 40 мВ и 0,08 Вт; номинальная мощность, допуск, температурный коэффициент и импульсный запас выбираются отдельно. Крупный пусковой ток может выйти за измерительный диапазон. [156]",
        "y": 83.33208465576172,
        "page": 54,
        "old": 15,
        "id": "A15-017",
        "source_ids": [
          "A15-017"
        ],
        "anchor": "A15-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Измерительные линии шунта прокладываются отдельно от силового падения на дорожках, когда создаётся собственная плата. Калибровка выполняется с известной нагрузкой. Нулевой ток при команде ON вызывает «действие не подтверждено», но ненулевой ток ещё не гарантирует свет на столе.",
        "y": 171.71102905273438,
        "page": 54,
        "old": 15,
        "id": "A15-018",
        "source_ids": [
          "A15-018"
        ],
        "anchor": "A15-018",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Кнопка, старт и отключение связи",
        "y": 251.50521850585938,
        "page": 54,
        "old": 15,
        "id": "A15-019",
        "source_ids": [
          "A15-019"
        ],
        "anchor": "A15-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "GPIO переводится в безопасное состояние до подключения к сети. Физическая кнопка обрабатывается локально с подавлением дребезга; её действие отправляется станции как событие и новая версия состояния. Сервер не имеет права незаметно отменять нажатие сразу после переподключения сохранённым ранее состоянием.",
        "y": 278.5228576660156,
        "page": 54,
        "old": 15,
        "id": "A15-020",
        "source_ids": [
          "A15-020"
        ],
        "anchor": "A15-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В профиле E0 при потере только Wi-Fi исправная прошивка сохраняет выход и локальное управление. При reset или потере питания обычное небистабильное реле может отпустить. После загрузки E0 начинает с OFF и BOOT_INHIBIT: автоматическое восстановление ON запрещено до явного освобождения режима. Это не обещание непрерывного света во время reset. Для такой гарантии нужен другой аппаратный профиль и отдельное испытание.",
        "y": 350.9267883300781,
        "page": 54,
        "old": 15,
        "id": "A15-021",
        "source_ids": [
          "A15-021"
        ],
        "anchor": "A15-021",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Приёмка Actuator",
        "y": 471.55059814453125,
        "page": 54,
        "old": 15,
        "id": "A15-022",
        "source_ids": [
          "A15-022"
        ],
        "anchor": "A15-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Проверить включение и выключение, перезапуск MCU, потерю Wi-Fi, повтор одинакового command_id, удержание кнопки, обрыв нагрузки и восстановление питания. Осциллографом или анализатором убедиться, что на старте нет короткого нежелательного импульса управления.",
        "y": 490.0118103027344,
        "page": 54,
        "old": 15,
        "id": "A15-023",
        "source_ids": [
          "A15-023"
        ],
        "anchor": "A15-023",
        "content_status": "active"
      },
      {
        "id": "R15-001",
        "kind": "h2",
        "text": "Контракт выхода E0",
        "source_ids": [
          "R15-001"
        ],
        "anchor": "R15-001",
        "content_status": "active"
      },
      {
        "id": "R15-002",
        "kind": "table",
        "rows": [
          [
            "Событие",
            "Выход и кнопка",
            "Возврат автоматики"
          ],
          [
            "Только Wi-Fi пропал",
            "Текущий уровень сохраняется; кнопка работает локально",
            "После snapshot и проверки ручного режима"
          ],
          [
            "Core перезапущен",
            "MCU продолжает работать",
            "Старый outbox не восстанавливает желание"
          ],
          [
            "Reset MCU / WDT / brownout",
            "Допускается отпускание; после старта OFF",
            "BOOT_INHIBIT до явного разрешения"
          ],
          [
            "Потеря питания MCU",
            "OFF у небистабильного реле",
            "Старые команды другой загрузки отклоняются"
          ],
          [
            "OTA: приём/запись",
            "Только в согласованном режиме обслуживания",
            "Кнопка доступна до reboot по проверке"
          ],
          [
            "OTA: reboot/rollback",
            "Есть измеряемое окно недоступности кнопки",
            "OFF + BOOT_INHIBIT, затем явное разрешение"
          ],
          [
            "Контакт сварился",
            "GPIO и кнопка не размыкают цепь",
            "Физическое снятие питания и ремонт"
          ]
        ],
        "source_ids": [
          "R15-002"
        ],
        "anchor": "R15-002",
        "content_status": "active"
      },
      {
        "id": "R15-003",
        "kind": "p",
        "text": "gpio_hold_en — возможная оптимизация конкретного reset-профиля, не гарантия на все сбросы и потерю питания. Её не включаем как условие безопасности E0. Нужны осциллограмма, reset reason и проверка ранней загрузки/освобождения hold. Бистабильное реле или отдельный аппаратный latch меняют отказную модель и требуют обратной связи по контакту. [153]",
        "source_ids": [
          "R15-003"
        ],
        "anchor": "R15-003",
        "content_status": "active"
      },
      {
        "id": "R15-004",
        "kind": "h2",
        "text": "Ручной режим живёт на исполнительном узле",
        "source_ids": [
          "R15-004"
        ],
        "anchor": "R15-004",
        "content_status": "active"
      },
      {
        "id": "R15-005",
        "kind": "p",
        "text": "Короткое нажатие физической кнопки меняет выход и включает MANUAL_HOLD без автоматического истечения в E0. В приложении явное «выключить вручную» продлевает намерение даже при уже OFF. Состояние ManualOverride содержит режим, значение, источник, manual_revision и правило освобождения. Повторный автоматический план после нового snapshot также отклоняется, пока режим действует.",
        "source_ids": [
          "R15-005"
        ],
        "anchor": "R15-005",
        "content_status": "active"
      },
      {
        "id": "R15-006",
        "kind": "p",
        "text": "Освобождение выполняется отдельным действием «Вернуть автоматику» либо назначенной длинной кнопкой, отличной от provisioning/reset. Оно меняет manual_revision, но само не исполняет старый ON: Core строит новое решение на свежих данных. При перезапуске MCU E0 использует BOOT_INHIBIT; при ошибке сохранения ручного режима автоматизация остаётся запрещённой. Защитное аппаратное отключение имеет более высокий приоритет, чем любой ручной режим.",
        "source_ids": [
          "R15-006"
        ],
        "anchor": "R15-006",
        "content_status": "active"
      },
      {
        "id": "R15-007",
        "kind": "p",
        "text": "Для E1 временный ручной режим можно добавить с видимым сроком: «свет выключен до 19:00, затем автоматика вернётся». При неизвестном времени или перезапуске временный запрет не снимается молча. Истечение во время UNKNOWN не создаёт действие: сначала нужны свежие входы. Режим E0 без таймера выбран ради однозначности, не потому, что временные режимы запрещены архитектурой.",
        "source_ids": [
          "R15-007"
        ],
        "anchor": "R15-007",
        "content_status": "active"
      }
    ],
    "origins": [
      "A15"
    ],
    "question": "Что именно подтверждает релейный узел?",
    "intro": "Есть три разных события: программа приняла команду, микроконтроллер изменил выход, лампа действительно засветилась. Между ними возможны обрыв провода, неисправный контакт или неправильное подключение нагрузки.\n\nНачнём с готового низковольтного релейного модуля и отдельной кнопки. Затем рассмотрим собственный драйвер катушки и измерение результата. Это позволяет изучить ту же архитектуру управления, не начиная с открытых цепей 230 В.",
    "id": "ch-15",
    "slug": "chapter-15"
  },
  {
    "number": 16,
    "title": "Собираем измерительный комплект",
    "group": "Практика",
    "units": [
      {
        "kind": "lead",
        "text": "Эти карты превращают функциональные схемы в проверяемые соединения. Неизвестная ревизия модуля сначала идентифицируется, а уже затем включается.",
        "y": 168.675537109375,
        "page": 122,
        "old": 37,
        "id": "A37-001",
        "source_ids": [
          "A37-001"
        ],
        "anchor": "A37-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Карта S-01. Узел Sense",
        "y": 236.06771850585938,
        "page": 122,
        "old": 37,
        "id": "A37-002",
        "source_ids": [
          "A37-002"
        ],
        "anchor": "A37-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Опорная сборка: ESP32-C6-DevKitC-1, готовые модули SHT45 и VEML7700, короткая I²C-шина. Для неё достаточно одного USB-источника питания платы и её вывода 3V3 для маломощных датчиков, если измеренный суммарный ток укладывается в возможности регулятора. Номинальное питание голого сенсора и допустимый VIN breakout — разные характеристики. На этапе унификации оба выбранных модуля подключаются к 3,3 В. [14][16][18]",
        "y": 263.0853576660156,
        "page": 122,
        "old": 37,
        "id": "A37-003",
        "source_ids": [
          "A37-003"
        ],
        "anchor": "A37-003",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Цепь",
            "Начало",
            "Конец",
            "Проверка без питания"
          ],
          [
            "W-S01",
            "C6 J1-1, 3V3",
            "VIN/VDD обоих модулей",
            "Нет замыкания на GND"
          ],
          [
            "W-S02",
            "C6 J1-15, GND",
            "GND обоих модулей",
            "Общий провод непрерывен"
          ],
          [
            "W-S03",
            "C6 J1-5, GPIO6",
            "SDA обоих модулей",
            "Нет связи с 5 В; учтены подтяжки"
          ],
          [
            "W-S04",
            "C6 J1-6, GPIO7",
            "SCL обоих модулей",
            "Нет связи с соседней строкой макетной платы"
          ],
          [
            "W-S05",
            "USB data-кабель",
            "USB платы",
            "Используется один согласованный путь питания"
          ]
        ],
        "y": 369.54,
        "page": 122,
        "old": 37,
        "id": "A37-004",
        "source_ids": [
          "A37-004"
        ],
        "anchor": "A37-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Вместо цветового описания «синий провод к синему» жгут получает номера W-S01…W-S05. На обоих концах подписываются сигнал и устройство. Метка на проводе важнее цвета: после замены кабеля электрический смысл должен сохраниться.",
        "y": 649.176025390625,
        "page": 122,
        "old": 37,
        "id": "A37-005",
        "source_ids": [
          "A37-005"
        ],
        "anchor": "A37-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сначала включается только плата. Затем присоединяется один датчик и проверяется его штатная операция чтения, а не только ACK адреса. После второго модуля повторно измеряется напряжение и фронт SCL. Устройства I²C иногда отвечают, но выдают неверные данные из-за питания, режима измерения или неправильного пересчёта регистра.",
        "y": 705.60498046875,
        "page": 122,
        "old": 37,
        "id": "A37-006",
        "source_ids": [
          "A37-006"
        ],
        "anchor": "A37-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Уровень шины в покое должен соответствовать выбранному питанию подтяжек. Логический анализатор полезен для адресов, ACK и последовательности команд; форму фронта и выбросы проверяет осциллограф. Приборы подключаются к низковольтной общей земле, а их допустимые уровни входа сверяются заранее.",
        "y": 57.42815399169922,
        "page": 123,
        "old": 37,
        "id": "A37-007",
        "source_ids": [
          "A37-007"
        ],
        "anchor": "A37-007",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Карта P-01. Присутствие без радара",
        "y": 137.22232055664062,
        "page": 123,
        "old": 37,
        "id": "A37-008",
        "source_ids": [
          "A37-008"
        ],
        "anchor": "A37-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первый вариант Presence использует PIR с документированным цифровым выходом, совместимым с 3,3-вольтовым входом MCU. Само питание PIR при этом может отличаться: оно выбирается по паспорту модуля. Надпись «PIR 5 В» не доказывает, что его выход обязательно равен 5 В либо безопасен для C6. Для рассматриваемого в учебном источнике модуля выход описан отдельно от питания. [20][21]",
        "y": 164.23995971679688,
        "page": 123,
        "old": 37,
        "id": "A37-009",
        "source_ids": [
          "A37-009"
        ],
        "anchor": "A37-009",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Цепь",
            "Соединение",
            "Условие"
          ],
          [
            "W-P01",
            "VCC PIR → его источник питания",
            "Полярность проверена по плате"
          ],
          [
            "W-P02",
            "GND PIR → GND C6",
            "Обязательно для неразвязанного сигнала"
          ],
          [
            "W-P03",
            "OUT PIR → GPIO18, J3-10",
            "Уровень и тип выхода подтверждены"
          ],
          [
            "W-P04",
            "Питание C6",
            "Не подаётся в обход согласованной USB-схемы"
          ]
        ],
        "y": 254.72,
        "page": 123,
        "old": 37,
        "id": "A37-010",
        "source_ids": [
          "A37-010"
        ],
        "anchor": "A37-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Вход может требовать подтяжки или её отключения в зависимости от типа выхода. При неизвестной схеме модуля сначала измеряется сигнал, затем выбирается согласование. Делитель не является универсальной заменой проверки: для open-collector, двунаправленной шины и высокоскоростного сигнала решения различаются.",
        "y": 443.2342529296875,
        "page": 123,
        "old": 37,
        "id": "A37-011",
        "source_ids": [
          "A37-011"
        ],
        "anchor": "A37-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Проверка PIR содержит прогрев после включения, движение поперёк зон, неподвижного человека и пустую комнату. Наличие импульса подтверждает движение в поле датчика, а не принадлежность человека определённой комнате и не его личность.",
        "y": 515.6382446289062,
        "page": 123,
        "old": 37,
        "id": "A37-012",
        "source_ids": [
          "A37-012"
        ],
        "anchor": "A37-012",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Карта P-02. Добавление радара",
        "y": 579.4574584960938,
        "page": 123,
        "old": 37,
        "id": "A37-013",
        "source_ids": [
          "A37-013"
        ],
        "anchor": "A37-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Радар SEN0395 — один из вариантов, а не обязательный модуль всей платформы. Его питание, логические уровни, UART-команды и временные режимы читаются по документации конкретной ревизии. Дешёвый модуль с похожим внешним видом не считается электрически и программно взаимозаменяемым. [22][23]",
        "y": 606.4750366210938,
        "page": 123,
        "old": 37,
        "id": "A37-014",
        "source_ids": [
          "A37-014"
        ],
        "anchor": "A37-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "MCU RX, GPIO20/J3-8, принимает TX радара. MCU TX, GPIO21/J3-7, идёт к RX радара. Названия TX и RX относятся к стороне, на которой написаны. Общая земля соединяется, если нет штатной гальванической развязки. Периферия RS-232 с отрицательными напряжениями или RS-485 с дифференциальными линиями сюда напрямую не подключается.",
        "y": 678.8789672851562,
        "page": 123,
        "old": 37,
        "id": "A37-015",
        "source_ids": [
          "A37-015"
        ],
        "anchor": "A37-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сначала UART проверяется отдельно от сетевой прошивки: версия, одна команда, один ответ, обработка частичного кадра. Далее добавляются неверная длина, обрыв потока и перезагрузка радара. Только после этого измерения участвуют в состоянии Occupied.",
        "y": 57.42815399169922,
        "page": 124,
        "old": 37,
        "id": "A37-016",
        "source_ids": [
          "A37-016"
        ],
        "anchor": "A37-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "PIR и радар лучше установить на разъёмных кронштейнах. Это позволяет менять направление без переделки электронного узла. В конечном корпусе нельзя перекрывать радар металлом, а PIR — произвольной прозрачной пластмассой: оптический тракт PIR определяется его линзой и рабочим диапазоном, а не видимой прозрачностью материала. [20][22]",
        "y": 113.85709381103516,
        "page": 124,
        "old": 37,
        "id": "A37-017",
        "source_ids": [
          "A37-017"
        ],
        "anchor": "A37-017",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Карта A-01. Реле и лампа 12 В",
        "y": 209.62625122070312,
        "page": 124,
        "old": 37,
        "id": "A37-018",
        "source_ids": [
          "A37-018"
        ],
        "anchor": "A37-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первый исполнитель — документированный готовый релейный модуль с драйвером катушки, входом управления, совместимым с C6, и подходящим DC-рейтингом контактов. VIN, GND и SIG подключаются по схеме этого модуля. GPIO10/J1-10 служит сигналом, но не питает катушку. Конкретный STEMMA-модуль рассмотрен как опорный пример, не как единственно доступный товар. [24][25]",
        "y": 236.64389038085938,
        "page": 124,
        "old": 37,
        "id": "A37-019",
        "source_ids": [
          "A37-019"
        ],
        "anchor": "A37-019",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Цепь",
            "Соединение",
            "Смысл"
          ],
          [
            "W-A01",
            "GPIO10 → SIG",
            "Только управляющий сигнал"
          ],
          [
            "W-A02",
            "GND C6 → GND модуля",
            "Возвратный путь управления"
          ],
          [
            "W-A03",
            "VIN модуля → допустимое питание",
            "Напряжение и ток по паспорту"
          ],
          [
            "W-A04",
            "+12 В адаптера → F1 → COM",
            "Предохранитель рядом с источником"
          ],
          [
            "W-A05",
            "NO → плюс лампы",
            "Лампа включается при замыкании"
          ],
          [
            "W-A06",
            "Минус лампы → минус адаптера",
            "Обратный провод нагрузки"
          ],
          [
            "W-A07",
            "Кнопка → GPIO18 и GND",
            "Локальное действие, с подтяжкой и подавлением дребезга"
          ]
        ],
        "y": 327.12,
        "page": 124,
        "old": 37,
        "id": "A37-020",
        "source_ids": [
          "A37-020"
        ],
        "anchor": "A37-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сухие контакты реле не требуют объединения минуса 12-вольтового адаптера с GND контроллера. Но добавление измерителя тока, неразвязанного датчика напряжения или другого интерфейса может создать такое соединение. Тогда схему доменов пересматривают; нельзя продолжать называть тракт развязанным только из-за наличия реле.",
        "y": 582.2503051757812,
        "page": 124,
        "old": 37,
        "id": "A37-021",
        "source_ids": [
          "A37-021"
        ],
        "anchor": "A37-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "При холодном старте выход удерживается в OFF аппаратной подтяжкой и правильной полярностью драйвера. Нельзя сначала переключить GPIO в выход с случайным значением, а потом рассчитывать, что задача сети успеет его исправить. Активный LOW модуль требует противоположной логики удержания; такую замену фиксируют как другую аппаратную конфигурацию.",
        "y": 670.6292724609375,
        "page": 124,
        "old": 37,
        "id": "A37-022",
        "source_ids": [
          "A37-022"
        ],
        "anchor": "A37-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Кнопка вызывает ту же предметную команду set или локальную явно определённую операцию, а не запись в GPIO параллельно с сетевым обработчиком. У выхода один владелец состояния. Для светового стенда ручная команда может создавать временное переопределение автоматики. Для будущего опасного привода аппаратный STOP проектируется отдельно и не заменяется кнопкой, которую опрашивает задача MCU.",
        "y": 57.42815399169922,
        "page": 125,
        "old": 37,
        "id": "A37-023",
        "source_ids": [
          "A37-023"
        ],
        "anchor": "A37-023",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Приёмка проводки",
        "y": 153.19732666015625,
        "page": 125,
        "old": 37,
        "id": "A37-024",
        "source_ids": [
          "A37-024"
        ],
        "anchor": "A37-024",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "После монтажа делаются фотографии обеих сторон платы, общий план и крупные планы клемм. Проверяются отсутствие выпавших жил, механическое удержание, запас изгиба, закрытые токоведущие контакты и отсутствие пути пережатия при закрытии крышки. Винтовая клемма и многожильный провод подбираются как соединение: способ оконцевания берётся из требований клеммы.",
        "y": 180.2149658203125,
        "page": 125,
        "old": 37,
        "id": "A37-025",
        "source_ids": [
          "A37-025"
        ],
        "anchor": "A37-025",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Подключение модулей «на горячую» не считается штатным, пока это не подтверждено схемой. Для первой сборки питание снимается перед изменением жгута. USB-кабель вынимается тоже: выключенный внешний адаптер не означает, что плата обесточена через все возможные пути.",
        "y": 268.5939025878906,
        "page": 125,
        "old": 37,
        "id": "A37-026",
        "source_ids": [
          "A37-026"
        ],
        "anchor": "A37-026",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Первая подача питания",
        "y": 357.2677307128906,
        "page": 125,
        "old": 37,
        "id": "A37-027",
        "source_ids": [
          "A37-027"
        ],
        "anchor": "A37-027",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для каждого узла отдельно: визуальный контроль → проверка сопротивлений и полярности → питание с контролем тока → измерение шин → диагностическая прошивка → один датчик → связь → исполнитель без нагрузки → нагрузка 12 В. Не перескакивать сразу к общему сценарию: иначе ошибка проводки и ошибка автоматики будут выглядеть одинаково.",
        "y": 375.7289123535156,
        "page": 125,
        "old": 37,
        "id": "A37-028",
        "source_ids": [
          "A37-028"
        ],
        "anchor": "A37-028",
        "content_status": "active"
      },
      {
        "id": "N-004",
        "kind": "h2",
        "text": "Не объединять две исправные сборки вслепую",
        "new": true,
        "source_ids": [
          "N-004"
        ],
        "anchor": "N-004",
        "content_status": "active"
      },
      {
        "id": "N-005",
        "kind": "p",
        "text": "До объединения запишите, откуда питается каждый модуль и где проходит общий провод сигнала. USB от ПК, отдельный адаптер и внешняя нагрузка могут образовать несколько путей тока. В готовой схеме должен быть один понятный путь для каждой цепи; решение об общей земле принимается по интерфейсу, а не по цвету провода.",
        "new": true,
        "source_ids": [
          "N-005"
        ],
        "anchor": "N-005",
        "content_status": "active"
      },
      {
        "id": "N-006",
        "kind": "p",
        "text": "После добавления следующего узла повторите прежние проверки. Например, с работающим радио и реле снова измерьте минимальное питание сенсора и проверьте обмен I²C. Так обнаруживается не только ошибка нового провода, но и влияние новой нагрузки на уже работавшую часть.",
        "new": true,
        "source_ids": [
          "N-006"
        ],
        "anchor": "N-006",
        "content_status": "active"
      },
      {
        "id": "R16-001",
        "kind": "h2",
        "text": "Что делает монтажную карту исполнимой",
        "source_ids": [
          "R16-001"
        ],
        "anchor": "R16-001",
        "content_status": "active"
      },
      {
        "id": "R16-002",
        "kind": "p",
        "text": "Перед электрической сборкой из вариантов выбирается один экземпляр BOM: фото маркировок и ревизий, напряжения каждого VIN, полярность IN, адаптер и нагрузка, держатель/F1, сечение провода, точные клеммы. Неопределённая строка «подать допустимое питание» считается открытым полем карты. Она не закрывается предположением о похожем товаре с маркетплейса.",
        "source_ids": [
          "R16-002"
        ],
        "anchor": "R16-002",
        "content_status": "active"
      },
      {
        "id": "R16-003",
        "kind": "p",
        "text": "Для S/P/A-карт добавляется проверка обратного питания через USB, GPIO, I²C и UART: измеряется отключённая шина при работающем соседнем модуле. Две логические схемы с общим GND не делают все режимы подачи питания безопасными. Особое внимание — USB от ПК одновременно с отдельным адаптером Actuator. В рабочем Actuator закрытый источник 5 В подаёт питание через выбранный штатный вход платы и согласованный жгут к драйверу реле; питание нагрузки 12 В проходит своей цепью. Конкретный вход, возвратный провод и допустимый ток фиксируются по схеме ревизии DevKit и модуля до включения. USB Hub не служит штатным источником Actuator. Сервисный USB подключается только после выбора проверенного способа исключить объединение источников: поддержанная платой развязка или документированное отключение одной питающей ветви. Произвольное удаление VBUS не обещает работоспособного USB-обмена. Приёмка проверяет кнопку с выключенным Hub и каждое разрешённое сочетание источников.",
        "source_ids": [
          "R16-003"
        ],
        "anchor": "R16-003",
        "content_status": "active"
      }
    ],
    "origins": [
      "A37"
    ],
    "question": "Что должно оказаться на столе и как проверить каждый провод?",
    "intro": "Монтажная карта связывает два конкретных контакта проводом и задаёт проверку перед подачей питания. Она дополняет общую электрическую схему точными обозначениями приобретённых деталей.\n\nSense, Presence и Actuator собирают отдельно. Сначала проверяют питание и один сигнал, затем добавляют следующий узел. Общую автоматизацию запускают после независимой проверки каждого участника. Названия контактов сверяют с аппаратной ревизией платы.",
    "id": "ch-16",
    "slug": "chapter-16"
  },
  {
    "number": 17,
    "title": "От макета к собственной плате",
    "group": "Практика",
    "units": [
      {
        "kind": "lead",
        "text": "Задача первой собственной PCB — сократить ненадёжные соединения. Сложность отладочной платы переносится в неё только при подтверждённой необходимости.",
        "y": 137.3555450439453,
        "page": 55,
        "old": 16,
        "id": "A16-001",
        "source_ids": [
          "A16-001"
        ],
        "anchor": "A16-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Разумная первая плата Koobique — носитель готового MCU-модуля или DevKit, разъёмы сенсоров, питание, кнопка и тестовые точки. Не нужно одновременно разводить RF, USB высокой сложности, собственный DC/DC и релейный исполнитель, если ещё не проверен базовый сценарий. Разделение Sense и силового узла помогает локализовать помехи и ошибки.",
        "y": 188.46142578125,
        "page": 55,
        "old": 16,
        "id": "A16-002",
        "source_ids": [
          "A16-002"
        ],
        "anchor": "A16-002",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A21",
        "caption": "Зонирование первой платы-носителя. На рисунке нет финальной трассировки; обозначены области, которые должны сохраниться в CAD.",
        "y": 284.6090393066406,
        "page": 55,
        "old": 16,
        "id": "A16-003",
        "source_ids": [
          "A16-003"
        ],
        "anchor": "A16-003",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Три уровня самостоятельности",
        "y": 648.5468139648438,
        "page": 55,
        "old": 16,
        "id": "A16-004",
        "source_ids": [
          "A16-004"
        ],
        "anchor": "A16-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "На первом уровне используются готовые модули и паяная монтажная плата. На втором — собственный носитель с фиксированными разъёмами и известной распиновкой. На третьем — модуль ESP32-C6-WROOM-1 / MINI-1 на своей плате с питанием и программированием. Переход на голый SoC имеет смысл только при понятной причине: ограничения размеров, питания или количества экземпляров.",
        "y": 675.564453125,
        "page": 55,
        "old": 16,
        "id": "A16-005",
        "source_ids": [
          "A16-005"
        ],
        "anchor": "A16-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Модуль сохраняет уже выполненную RF-часть, но требует соблюдения правил размещения антенны и питания. Документ Espressif содержит рекомендации для ESP32-C6 по слоям, земле, антенне и периферии; он важнее фотографии похожего устройства. [81]",
        "y": 57.42815399169922,
        "page": 56,
        "old": 16,
        "id": "A16-006",
        "source_ids": [
          "A16-006"
        ],
        "anchor": "A16-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Схема раньше дорожек",
        "y": 121.24732971191406,
        "page": 56,
        "old": 16,
        "id": "A16-007",
        "source_ids": [
          "A16-007"
        ],
        "anchor": "A16-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сначала составляются схема и таблица сетей: имя сигнала, напряжение, направление, безопасное состояние и разъём. Затем назначаются footprint по точному компоненту. Совпадение названия корпуса не гарантирует совпадение нумерации выводов. Каждый новый footprint проверяется по datasheet и пробной распечатке 1:1.",
        "y": 148.26495361328125,
        "page": 56,
        "old": 16,
        "id": "A16-008",
        "source_ids": [
          "A16-008"
        ],
        "anchor": "A16-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "ERC находит часть ошибок схемы, DRC — часть ошибок платы. Ни одна проверка не знает, что разъём на фотографии продавца установлен зеркально или что датчик нагревается соседним стабилизатором. KiCad включает связанный процесс схема → плата → проверка → производственные файлы; учебник выбранной версии полезен для первого прохода. [27]",
        "y": 220.66888427734375,
        "page": 56,
        "old": 16,
        "id": "A16-009",
        "source_ids": [
          "A16-009"
        ],
        "anchor": "A16-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Два или четыре слоя",
        "y": 316.4380798339844,
        "page": 56,
        "old": 16,
        "id": "A16-010",
        "source_ids": [
          "A16-010"
        ],
        "anchor": "A16-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Простой носитель с короткой I²C и готовым модулем можно исследовать на двух слоях. При плотной цифровой разводке и необходимости устойчивой опорной земли четыре слоя часто дают более предсказуемую конструкцию. Это не правило «четыре всегда лучше»: важны реальный stackup производителя, расстояния между слоями и непрерывность возвратного пути.",
        "y": 343.4557189941406,
        "page": 56,
        "old": 16,
        "id": "A16-011",
        "source_ids": [
          "A16-011"
        ],
        "anchor": "A16-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Высокочастотный возвратный ток стремится идти рядом с сигнальной трассой по опорной плоскости. Разрез земли под трассой заставляет его обходить щель, увеличивая петлю. Поэтому функциональные зоны лучше разделять размещением и маршрутами токов, а не произвольным разрезанием GND под каждой подсистемой.",
        "y": 431.83465576171875,
        "page": 56,
        "old": 16,
        "id": "A16-012",
        "source_ids": [
          "A16-012"
        ],
        "anchor": "A16-012",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A22",
        "caption": "Почему непрерывная опорная плоскость полезнее случайно разделённой «аналоговой» и «цифровой» земли. Это принципиальная иллюстрация возвратного пути.",
        "y": 56.69291687011719,
        "page": 57,
        "old": 16,
        "id": "A16-013",
        "source_ids": [
          "A16-013"
        ],
        "anchor": "A16-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Питание и развязка",
        "y": 396.6778869628906,
        "page": 57,
        "old": 16,
        "id": "A16-014",
        "source_ids": [
          "A16-014"
        ],
        "anchor": "A16-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Развязывающие конденсаторы ставятся близко к соответствующим выводам с коротким возвратом, а не группируются в свободном углу платы. Источник питания выбирается по пиковому току и переходной характеристике. Широкая дорожка не устраняет индуктивность длинного пути; измерять нужно напряжение у потребителя.",
        "y": 423.695556640625,
        "page": 57,
        "old": 16,
        "id": "A16-015",
        "source_ids": [
          "A16-015"
        ],
        "anchor": "A16-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Силовой ток реле или лампы не пропускают через тонкий общий участок, по которому возвращается чувствительное измерение. USB, UART и GPIO на внешних разъёмах требуют оценки электростатики и ошибок подключения. Защитный элемент выбирается по напряжению, ёмкости и энергии, а не по одному слову TVS.",
        "y": 496.0994873046875,
        "page": 57,
        "old": 16,
        "id": "A16-016",
        "source_ids": [
          "A16-016"
        ],
        "anchor": "A16-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Тестовые точки и ремонт",
        "y": 575.8936767578125,
        "page": 57,
        "old": 16,
        "id": "A16-017",
        "source_ids": [
          "A16-017"
        ],
        "anchor": "A16-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "На плате доступны GND, вход питания, 3V3, reset, boot и диагностический UART/USB. Точки подписаны так, чтобы к ним можно было подключиться уже в корпусе. Силовой узел имеет возможность безопасно отключить нагрузку для прошивки. У реле и разъёмов оставляется место для паяльника и вынимания штекера.",
        "y": 602.9113159179688,
        "page": 57,
        "old": 16,
        "id": "A16-018",
        "source_ids": [
          "A16-018"
        ],
        "anchor": "A16-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первых плат полезны крупные пассивные компоненты и доступная пайка. Мелкий корпус ради эстетики повышает риск ошибки и не делает единичный экземпляр качественнее. Предусмотренные нулевые резисторы/перемычки могут облегчить измерение тока и изоляцию неисправного домена.",
        "y": 675.3152465820312,
        "page": 57,
        "old": 16,
        "id": "A16-019",
        "source_ids": [
          "A16-019"
        ],
        "anchor": "A16-019",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Где заказать из Тулы",
        "y": 56.314456939697266,
        "page": 58,
        "old": 16,
        "id": "A16-020",
        "source_ids": [
          "A16-020"
        ],
        "anchor": "A16-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Местный подрядчик необязателен. Резонит публикует услугу изготовления опытных плат и варианты доставки; Электроконнект — изготовление единичных прототипов. Это подтверждённые каналы для запроса расчёта, а не обещание конкретной цены, сроков или доступности монтажа для нашего заказа. Доставку в Тулу, минимальный заказ, работу с физлицом и монтаж уточняют перед оплатой. [28][29][30]",
        "y": 83.33208465576172,
        "page": 58,
        "old": 16,
        "id": "A16-021",
        "source_ids": [
          "A16-021"
        ],
        "anchor": "A16-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для расчёта нужны Gerber/Excellon, размеры, число слоёв, толщина, медь, покрытие и требования. Для монтажа дополнительно нужны BOM, pick-and-place, сборочный чертёж, полярности и перечень допустимых замен. Gerber-only не содержит всех данных о том, что должно быть установлено.",
        "y": 171.71102905273438,
        "page": 58,
        "old": 16,
        "id": "A16-022",
        "source_ids": [
          "A16-022"
        ],
        "anchor": "A16-022",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Минимальный выпуск E0",
        "y": 260.3848571777344,
        "page": 58,
        "old": 16,
        "id": "A16-023",
        "source_ids": [
          "A16-023"
        ],
        "anchor": "A16-023",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Заказать сначала малый набор носителей, а не все будущие устройства. Собрать одну плату, проверить питание без MCU, затем с MCU, потом с датчиками. Сохранить фотографии, напряжения, потребление и список доработок. Вторую плату собирать по исправленной инструкции, чтобы проверить воспроизводимость.",
        "y": 278.8460388183594,
        "page": 58,
        "old": 16,
        "id": "A16-024",
        "source_ids": [
          "A16-024"
        ],
        "anchor": "A16-024",
        "content_status": "active"
      }
    ],
    "origins": [
      "A16"
    ],
    "question": "Когда макет действительно пора переносить на плату?",
    "intro": "Своя печатная плата полезна, когда соединения уже понятны, а случайные провода стали главным источником ошибок. До этого она лишь закрепит недопроверенное решение в меди.\n\nПервой собственной платой может быть не полный компьютер или радиомодуль, а аккуратный носитель готовых узлов: питание, разъёмы, кнопка, защита и крепление. Это сохраняет возможность менять микроконтроллер и датчики без переделки всех сложных частей.",
    "id": "ch-17",
    "slug": "chapter-17"
  },
  {
    "number": 18,
    "title": "Прошивка, которая переживает сеть",
    "group": "Прошивки",
    "units": [
      {
        "kind": "lead",
        "text": "Язык снижает часть рисков памяти, но не отменяет аппаратные ограничения и проверку всего стека.",
        "y": 168.675537109375,
        "page": 59,
        "old": 17,
        "id": "A17-001",
        "source_ids": [
          "A17-001"
        ],
        "anchor": "A17-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "У ESP есть два принципиальных Rust-пути. Первый использует ESP-IDF через Rust-обвязки и получает сетевой стек, RTOS и сервисы SDK. Второй строится на esp-hal без стандартной библиотеки и даёт больше контроля над памятью и исполнением. Для Koobique важно не выбрать «самый чистый Rust», а получить проверяемое устройство с безопасной сетью, обновлением и локальным выходом.",
        "y": 219.78143310546875,
        "page": 59,
        "old": 17,
        "id": "A17-002",
        "source_ids": [
          "A17-002"
        ],
        "anchor": "A17-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "ESP-IDF или no_std",
        "y": 315.55059814453125,
        "page": 59,
        "old": 17,
        "id": "A17-003",
        "source_ids": [
          "A17-003"
        ],
        "anchor": "A17-003",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Путь",
            "Сильная сторона",
            "Инженерная цена",
            "Где начать"
          ],
          [
            "Rust + ESP-IDF",
            "Богатый SDK, provisioning, OTA, TLS",
            "C/FFI, несколько версий инструментов, сопровождение обвязок",
            "Первый Wi-Fi-узел"
          ],
          [
            "esp-hal / no_std",
            "Контроль ресурсов и аппаратуры",
            "Проверка зрелости конкретного радио, TLS и обновления",
            "Исследовательская ветка или простой узел"
          ],
          [
            "Другой MCU + Rust",
            "Выбор питания и периферии под задачу",
            "Новый toolchain и BSP",
            "Будущее батарейное устройство"
          ]
        ],
        "y": 344.67,
        "page": 59,
        "old": 17,
        "id": "A17-004",
        "source_ids": [
          "A17-004"
        ],
        "anchor": "A17-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Репозиторий Rust-обвязок ESP-IDF прямо описывает ограничения сопровождения сообщества и возможное отставание; официальный esp-hal имеет другую модель развития. The Rust on ESP Book предупреждает об изменениях нестабильных API. Это не причина отказаться от Rust, а причина фиксировать toolchain и испытывать выбранную комбинацию. [31][32][33]",
        "y": 532.5162353515625,
        "page": 59,
        "old": 17,
        "id": "A17-005",
        "source_ids": [
          "A17-005"
        ],
        "anchor": "A17-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первого Wi-Fi-профиля предлагаем Rust + ESP-IDF с проверенной версией SDK. Ветка no_std выполняет тот же контракт устройства, но не должна задерживать сборку основного стенда. Общие типы сообщений и тестовые векторы не зависят от конкретной RTOS.",
        "y": 620.8952026367188,
        "page": 59,
        "old": 17,
        "id": "A17-006",
        "source_ids": [
          "A17-006"
        ],
        "anchor": "A17-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Память — измерять в худший момент",
        "y": 56.314456939697266,
        "page": 60,
        "old": 17,
        "id": "A17-007",
        "source_ids": [
          "A17-007"
        ],
        "anchor": "A17-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Пик может возникнуть не при обычной телеметрии, а во время BLE-сопряжения, TLS-handshake и OTA. Заранее освобождённая BLE-память после настройки полезна, если BLE больше не требуется. Внутренние лимиты сообщений должны быть значительно меньше всей доступной RAM; буфер «на всякий случай 64 КБ» в каждом слое быстро исчерпывает MCU.",
        "y": 83.33208465576172,
        "page": 60,
        "old": 17,
        "id": "A17-008",
        "source_ids": [
          "A17-008"
        ],
        "anchor": "A17-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Mbed TLS в актуальной ESP-IDF имеет профили конфигурации; минимальный preset может не включать TLS 1.3, хотя другая конфигурация его поддерживает. Поэтому профиль Koobique фиксирует разрешённую версию и проверяет реальный handshake с Rustls, а не полагается на название SDK. [34]",
        "y": 171.71102905273438,
        "page": 60,
        "old": 17,
        "id": "A17-009",
        "source_ids": [
          "A17-009"
        ],
        "anchor": "A17-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Нужны измерения свободной памяти, крупнейшего свободного блока, пиков стека и отказов выделения. Успешный запуск один раз не доказывает отсутствие фрагментации после тысячи переподключений.",
        "y": 244.11495971679688,
        "page": 60,
        "old": 17,
        "id": "A17-010",
        "source_ids": [
          "A17-010"
        ],
        "anchor": "A17-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Задачи и приоритеты",
        "y": 307.93414306640625,
        "page": 60,
        "old": 17,
        "id": "A17-011",
        "source_ids": [
          "A17-011"
        ],
        "anchor": "A17-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Локальный владелец выхода обрабатывает короткие события кнопки и команды. Опрос датчика имеет ограничение времени. Сетевая задача занимается соединением и передачей. Обновление выполняется отдельным режимом с согласованным безопасным состоянием. Тяжёлое форматирование логов и запись flash не происходят в обработчике прерывания.",
        "y": 334.9517822265625,
        "page": 60,
        "old": 17,
        "id": "A17-012",
        "source_ids": [
          "A17-012"
        ],
        "anchor": "A17-012",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A23",
        "caption": "Предлагаемое разделение задач. Ограничения очередей и владение выходом важнее количества потоков.",
        "y": 431.0993957519531,
        "page": 60,
        "old": 17,
        "id": "A17-013",
        "source_ids": [
          "A17-013"
        ],
        "anchor": "A17-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "ISR фиксирует минимальные данные и передаёт событие. volatile не заменяет атомарность и синхронизацию. Общий изменяемый доступ к периферии ограничивается одним владельцем или явно синхронизированным интерфейсом. Блокировка, удерживаемая во время сетевого ожидания, может остановить кнопку — это недопустимая граница ответственности.",
        "y": 57.42815399169922,
        "page": 61,
        "old": 17,
        "id": "A17-014",
        "source_ids": [
          "A17-014"
        ],
        "anchor": "A17-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Ошибки как часть интерфейса",
        "y": 153.19732666015625,
        "page": 61,
        "old": 17,
        "id": "A17-015",
        "source_ids": [
          "A17-015"
        ],
        "anchor": "A17-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Драйвер возвращает различимые ошибки: нет ответа, неверный CRC, превышен диапазон, устаревшие данные, периферия не инициализирована. В зависимости от причины выполняется повтор, переинициализация шины или перевод канала в недоступный режим. Бесконечный retry без задержки способен перегрузить шину и журнал.",
        "y": 180.2149658203125,
        "page": 61,
        "old": 17,
        "id": "A17-016",
        "source_ids": [
          "A17-016"
        ],
        "anchor": "A17-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Watchdog должен проверять продвижение задач, а не просто обслуживаться из отдельного живого таймера. Если управление выходом зависло, но таймер продолжает «кормить собаку», защита не выполняет задачу. После reset сохраняется причина, насколько это поддерживается аппаратурой; она передаётся станции для диагностики.",
        "y": 252.618896484375,
        "page": 61,
        "old": 17,
        "id": "A17-017",
        "source_ids": [
          "A17-017"
        ],
        "anchor": "A17-017",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Flash и обновление",
        "y": 332.4130859375,
        "page": 61,
        "old": 17,
        "id": "A17-018",
        "source_ids": [
          "A17-018"
        ],
        "anchor": "A17-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Разделы для конфигурации, двух образов и служебных данных определяются после измерения размера прошивки. Запись каждого измерения во flash не допускается по умолчанию. Частые счётчики хранятся в RAM или агрегируются; критичная идентичность и параметры имеют отдельную политику записи.",
        "y": 359.43072509765625,
        "page": 61,
        "old": 17,
        "id": "A17-019",
        "source_ids": [
          "A17-019"
        ],
        "anchor": "A17-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Secure Boot и защита flash исследуются на отладочных устройствах с пониманием необратимых настроек. Производственные eFuse нельзя прожигать до проверки восстановления и хранения ключей. Отладочный доступ удобен на стенде, но для постоянно используемого устройства является частью модели угроз. [35][36]",
        "y": 431.83465576171875,
        "page": 61,
        "old": 17,
        "id": "A17-020",
        "source_ids": [
          "A17-020"
        ],
        "anchor": "A17-020",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Gate прошивки",
        "y": 520.5084838867188,
        "page": 61,
        "old": 17,
        "id": "A17-021",
        "source_ids": [
          "A17-021"
        ],
        "anchor": "A17-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Узел должен пережить многократные Wi-Fi-обрывы, отказ датчика, переполнение входного сообщения, повтор команды и перезапуск во время обмена. Физическая кнопка и безопасный выход проверяются не только при исправной сети. Версии Rust, ESP-IDF, зависимостей и sdkconfig сохраняются вместе с результатами.",
        "y": 538.9696655273438,
        "page": 61,
        "old": 17,
        "id": "A17-022",
        "source_ids": [
          "A17-022"
        ],
        "anchor": "A17-022",
        "content_status": "active"
      },
      {
        "id": "R18-001",
        "kind": "h2",
        "text": "Отладочная плата и защищённый прибор — два режима",
        "source_ids": [
          "R18-001"
        ],
        "anchor": "R18-001",
        "content_status": "active"
      },
      {
        "id": "R18-002",
        "kind": "table",
        "rows": [
          [
            "DEV / E0",
            "APPLIANCE / после проверки"
          ],
          [
            "Отладка доступна; тестовые секреты",
            "Secure Boot v2 и Flash Encryption с проверенным workflow"
          ],
          [
            "Нет обещания стойкости при физическом доступе",
            "NVS Encryption, контроль debug/download-интерфейсов"
          ],
          [
            "Рабочий ключ создаётся на узле",
            "Неэкспортируемость/защита ключа подтверждены настройкой"
          ],
          [
            "Восстановление через кабель допустимо",
            "Recovery и OTA испытаны до необратимых eFuse"
          ]
        ],
        "source_ids": [
          "R18-002"
        ],
        "anchor": "R18-002",
        "content_status": "active"
      },
      {
        "id": "R18-003",
        "kind": "p",
        "text": "NVS-шифрование не следует считать автоматическим следствием Flash Encryption. Выбранная схема и ключи фиксируются отдельно. Конфиденциальность flash также не доказывает защиту от возврата старого корректно зашифрованного образа: если в модели угроз есть физический rollback, потребуется отдельная защита состояния владения. Для E0 этот класс атак не объявляется решённым. [150][151]",
        "source_ids": [
          "R18-003"
        ],
        "anchor": "R18-003",
        "content_status": "active"
      },
      {
        "id": "R18-004",
        "kind": "p",
        "text": "Ключ и boot_id генерируются на самом MCU при выполненных условиях аппаратной энтропии. В ESP-IDF качество RNG зависит от включённых источников; вызов esp_fill_random сразу после старта приложения сам по себе не доказывает достаточную случайность. Случайный boot_id — часть защиты от старой команды, поэтому тест генерации идентичности входит в обязательный опыт. [152]",
        "source_ids": [
          "R18-004"
        ],
        "anchor": "R18-004",
        "content_status": "active"
      },
      {
        "id": "R18-005",
        "kind": "h2",
        "text": "Первый технологический опыт — одна зафиксированная связка",
        "source_ids": [
          "R18-005"
        ],
        "anchor": "R18-005",
        "content_status": "active"
      },
      {
        "id": "R18-006",
        "kind": "p",
        "text": "До остальной прошивки проверяется один C6: выбранные Rust toolchain, esp-idf-sys/hal/svc, SDK и sdkconfig; BLE с уникальным provisioning-секретом; TLS 1.3 с Core; одна команда; кнопка под нагрузкой; прерванное OTA. Измеряют min-free heap, largest-free-block, стеки, размер подписанного образа, паузы flash и задержку кнопки. Эти версии пока не названы «проверенными» — карточка заполняется только выводами реальной сборки.",
        "source_ids": [
          "R18-006"
        ],
        "anchor": "R18-006",
        "content_status": "active"
      },
      {
        "id": "R18-007",
        "kind": "p",
        "text": "Одновременность режимов ограничена: provisioning и OTA не работают вместе; не более одного bulk-сеанса; после onboarding BLE освобождается, если профиль это позволяет. Неудача опыта сначала приводит к пересмотру конкурентности и SDK, а не к выводу, что любой Rust-путь слишком тяжёлый.",
        "source_ids": [
          "R18-007"
        ],
        "anchor": "R18-007",
        "content_status": "active"
      }
    ],
    "origins": [
      "A17"
    ],
    "question": "Как прошивка остаётся полезной без станции?",
    "intro": "У прошивки несколько независимых обязанностей: измерять, обслуживать физический выход, поддерживать связь и обновляться. Если одна большая функция ждёт Wi-Fi, остальные не должны останавливаться вместе с ней.\n\nRust помогает описать границы и владение данными, но надёжность появляется из архитектуры задач, ограниченных буферов и понятных состояний. Кнопка, стартовый уровень реле и реакция на потерю связи проверяются раньше красивой телеметрии.",
    "id": "ch-18",
    "slug": "chapter-18"
  },
  {
    "number": 19,
    "title": "Что происходит после подачи питания",
    "group": "Linux",
    "units": [
      {
        "kind": "lead",
        "text": "Первому хабу нужен обслуживаемый системный образ. Разработка собственного ядра потребовала бы отдельного обоснования и вышла бы за задачу начальной операционной платформы.",
        "y": 137.3555450439453,
        "page": 62,
        "old": 18,
        "id": "A18-001",
        "source_ids": [
          "A18-001"
        ],
        "anchor": "A18-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Koobique OS — продуктовая платформа поверх Linux. Linux управляет процессами, памятью, накопителями, радио и аудио. Rust-core интерпретирует физические данные и оркестрирует устройства. Такое разделение сохраняет контроль над системой, не требуя переписать USB, Wi-Fi и планировщик.",
        "y": 188.23130798339844,
        "page": 62,
        "old": 18,
        "id": "A18-002",
        "source_ids": [
          "A18-002"
        ],
        "anchor": "A18-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Системная основа",
        "y": 261.6729431152344,
        "page": 62,
        "old": 18,
        "id": "A18-003",
        "source_ids": [
          "A18-003"
        ],
        "anchor": "A18-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первого x86-экземпляра предлагается стабильный Debian без графического рабочего стола и с фиксированным набором пакетов. Это облегчает диагностику, обновление драйверов и работу с инструментами. Для более управляемого образа позже сравниваются Buildroot и готовая A/B-инфраструктура. Малый размер образа полезен, но не должен стоить потери воспроизводимого восстановления. [37][90]",
        "y": 288.46044921875,
        "page": 62,
        "old": 18,
        "id": "A18-004",
        "source_ids": [
          "A18-004"
        ],
        "anchor": "A18-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Необходимые службы: менеджер сети, SSH, синхронизация времени при наличии источника, журнал, Koobique-core, ограниченный привилегированный helper, аудиослужба и опциональные inference-workers. Нет обязательного Node.js, браузера, Kubernetes или Redis. Контейнеры могут использоваться для сборки и испытаний; их наличие на самой станции — отдельное решение.",
        "y": 371.30401611328125,
        "page": 62,
        "old": 18,
        "id": "A18-005",
        "source_ids": [
          "A18-005"
        ],
        "anchor": "A18-005",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Последовательность загрузки",
        "y": 56.314456939697266,
        "page": 63,
        "old": 18,
        "id": "A18-006",
        "source_ids": [
          "A18-006"
        ],
        "anchor": "A18-006",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A24",
        "caption": "Логическая последовательность готовности. API не объявляет все устройства доступными сразу после запуска процесса.",
        "y": 91.10079193115234,
        "page": 63,
        "old": 18,
        "id": "A18-007",
        "source_ids": [
          "A18-007"
        ],
        "anchor": "A18-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "UEFI и загрузчик передают управление ядру. Драйверы поднимают накопитель и сеть. Системные службы запускают Core после доступности обязательных ресурсов. Core читает конфигурацию, отмечает восстановленные наблюдения как потенциально устаревшие, открывает API и ждёт свежего состояния узлов.",
        "y": 487.6859130859375,
        "page": 63,
        "old": 18,
        "id": "A18-008",
        "source_ids": [
          "A18-008"
        ],
        "anchor": "A18-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Готовность разбивается на уровни: процесс жив; конфигурация читается; локальный API доступен; сеть работает; конкретный узел синхронизирован. Один зелёный индикатор «всё хорошо» скрывает слишком много. Приложение должно объяснять, что именно ещё не готово.",
        "y": 555.491455078125,
        "page": 63,
        "old": 18,
        "id": "A18-009",
        "source_ids": [
          "A18-009"
        ],
        "anchor": "A18-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Драйверы и идентификация оборудования",
        "y": 628.93310546875,
        "page": 63,
        "old": 18,
        "id": "A18-010",
        "source_ids": [
          "A18-010"
        ],
        "anchor": "A18-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Wi-Fi проверяется по PCI/USB ID и версии firmware. Аудио — по реально доступным каналам, частотам и поведению USB. Датчики CPU/платы — по драйверу hwmon и смыслу каждого показания. Например, k10temp документирует температурные каналы AMD; это не даёт права трактовать любое число hwmon как температуру воздуха. [38][39][40]",
        "y": 655.7206420898438,
        "page": 63,
        "old": 18,
        "id": "A18-011",
        "source_ids": [
          "A18-011"
        ],
        "anchor": "A18-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "На ARM важны Device Tree и точная аппаратная ревизия. Неверное описание контакта или периферии может выглядеть как ошибка пользовательской программы. На типичной x86-платформе многие устройства описываются иначе; копировать инструкции настройки GPIO с Pi на Mini-ITX нельзя. [74]",
        "y": 723.5261840820312,
        "page": 63,
        "old": 18,
        "id": "A18-012",
        "source_ids": [
          "A18-012"
        ],
        "anchor": "A18-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Стабильные имена устройств формируются через udev по свойствам, а не по случайному /dev/ttyUSB0. Два подключённых ESP-модуля могут поменяться номерами после перезагрузки. Ошибка идентификации здесь опаснее отсутствия соединения: программа может отправить команду не тому исполнительному узлу.",
        "y": 57.198036193847656,
        "page": 64,
        "old": 18,
        "id": "A18-013",
        "source_ids": [
          "A18-013"
        ],
        "anchor": "A18-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Изоляция процессов",
        "y": 130.63967895507812,
        "page": 64,
        "old": 18,
        "id": "A18-014",
        "source_ids": [
          "A18-014"
        ],
        "anchor": "A18-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Core работает без root. Доступ к USB, Bluetooth и аудио выдаётся конкретным сервисам. Привилегированный helper принимает узкий набор структурированных запросов: применить сетевой профиль, переключить образ, собрать диагностические сведения. Он не исполняет произвольную строку shell из приложения.",
        "y": 157.42721557617188,
        "page": 64,
        "old": 18,
        "id": "A18-015",
        "source_ids": [
          "A18-015"
        ],
        "anchor": "A18-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "systemd/cgroup позволяют ограничить память, CPU и другие ресурсы служб. Speech-worker получает меньший приоритет доступности, чем управление. Но ограничения не должны быть настолько жёсткими, чтобы постоянно вызывать перезапуски и новую загрузку модели. Нужен запас и ограничение очереди запросов. [41]",
        "y": 225.23275756835938,
        "page": 64,
        "old": 18,
        "id": "A18-016",
        "source_ids": [
          "A18-016"
        ],
        "anchor": "A18-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Время, журнал и диск",
        "y": 298.67437744140625,
        "page": 64,
        "old": 18,
        "id": "A18-017",
        "source_ids": [
          "A18-017"
        ],
        "anchor": "A18-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Относительные таймеры используют монотонные часы. Календарные расписания — часовую зону и явно обработанные скачки времени. После включения без интернета RTC помогает восстановить приблизительное время, но его точность проверяется. Истечение сертификатов не решается отключением всей проверки доверия.",
        "y": 325.4619140625,
        "page": 64,
        "old": 18,
        "id": "A18-018",
        "source_ids": [
          "A18-018"
        ],
        "anchor": "A18-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Журнал имеет лимит объёма, ротацию и маскирование секретов. Пароль Wi-Fi, токен восстановления и аудиозапись не должны попадать в общий debug-log. При заполнении диска система сокращает диагностические данные раньше, чем теряет возможность сохранить критическую конфигурацию. Для этого требуется запас места и отдельные пороги.",
        "y": 393.2674255371094,
        "page": 64,
        "old": 18,
        "id": "A18-019",
        "source_ids": [
          "A18-019"
        ],
        "anchor": "A18-019",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Оптимизация без слепых отключений",
        "y": 466.7091064453125,
        "page": 64,
        "old": 18,
        "id": "A18-020",
        "source_ids": [
          "A18-020"
        ],
        "anchor": "A18-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сначала профилируется штатная система. Проверяются CPU governor, задержка пробуждения, нагрузка IRQ, запись диска и влияние аудио. Затем меняется один параметр и повторяется полный тест. Отключение защиты памяти, TLS-проверки или mitigations ради микросекунд не считается допустимой оптимизацией качества.",
        "y": 493.4966125488281,
        "page": 64,
        "old": 18,
        "id": "A18-021",
        "source_ids": [
          "A18-021"
        ],
        "anchor": "A18-021",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Системная приёмка",
        "y": 578.6525268554688,
        "page": 64,
        "old": 18,
        "id": "A18-022",
        "source_ids": [
          "A18-022"
        ],
        "anchor": "A18-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "После холодной загрузки проверить сеть, время, БД, USB-идентичности и локальный API. Остановить каждый необязательный worker и убедиться, что Core остаётся доступным. Проверить возвращение питания, свободное место, журнал причины сбоя и локальный SSH без внешнего сервиса.",
        "y": 597.1137084960938,
        "page": 64,
        "old": 18,
        "id": "A18-023",
        "source_ids": [
          "A18-023"
        ],
        "anchor": "A18-023",
        "content_status": "active"
      },
      {
        "id": "R19-001",
        "kind": "h2",
        "text": "Служба должна загрузиться даже без роутера",
        "source_ids": [
          "R19-001"
        ],
        "anchor": "R19-001",
        "content_status": "active"
      },
      {
        "id": "R19-002",
        "kind": "p",
        "text": "Core стартует с локальным хранилищем и диагностикой без ожидания network-online.target. Появление адреса и смена сети обрабатываются как события. Для systemd выбираются Type=notify, WatchdogSec и ограниченные перезапуски; уведомление watchdog выдаётся только при продвижении критических задач. Слепой таймер, живущий отдельно от зависшего исполнителя, не подтверждает здоровье службы. [160][161]",
        "source_ids": [
          "R19-002"
        ],
        "anchor": "R19-002",
        "content_status": "active"
      },
      {
        "id": "R19-003",
        "kind": "p",
        "text": "В карте установки: Restore on AC Power Loss в UEFI/BIOS, политика сна H0, журнальные квоты, права на /data, nftables, SSH по ключам и отдельная сервисная учётная запись. Доступ к отладке не означает постоянную публикацию SSH на внешнем интерфейсе. Сначала проверяются локальный запуск и восстановление, затем удалённый путь.",
        "source_ids": [
          "R19-003"
        ],
        "anchor": "R19-003",
        "content_status": "active"
      }
    ],
    "origins": [
      "A18"
    ],
    "question": "Из каких ступеней состоит запуск станции?",
    "intro": "Подача питания не означает немедленную готовность комнаты. Сначала запускается загрузчик, затем ядро Linux и драйверы, потом системные службы и Koobique. После этого ещё нужно восстановить сеть и сверить состояние устройств.\n\nМы будем различать эти ступени в журнале и интерфейсе. Тогда сообщение «станция запускается» можно заменить полезной причиной: накопитель доступен, локальный API работает, но один узел ещё не подтвердил состояние.",
    "id": "ch-19",
    "slug": "chapter-19"
  },
  {
    "number": 20,
    "title": "Устройство программной платформы",
    "group": "Rust",
    "units": [
      {
        "kind": "lead",
        "text": "Качество ядра определяется тем, можно ли объяснить каждую команду и ограничить последствия каждого входа.",
        "y": 168.675537109375,
        "page": 65,
        "old": 19,
        "id": "A19-001",
        "source_ids": [
          "A19-001"
        ],
        "anchor": "A19-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В центре Koobique находится один процесс с несколькими явными подсистемами: реестр, наблюдения, оценки состояния, политики, исполнитель команд, адаптеры и хранилище. Внешний API и собственный протокол не должны обходить общий путь проверки прав и ограничений. Голос, приложение и локальная автоматизация создают разные источники намерений, но не три независимых механизма включения реле.",
        "y": 219.78143310546875,
        "page": 65,
        "old": 19,
        "id": "A19-002",
        "source_ids": [
          "A19-002"
        ],
        "anchor": "A19-002",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A25",
        "caption": "Слои Koobique OS. Верхний уровень не получает прямой доступ к GPIO или к привилегированным функциям Linux.",
        "y": 315.9290466308594,
        "page": 65,
        "old": 19,
        "id": "A19-003",
        "source_ids": [
          "A19-003"
        ],
        "anchor": "A19-003",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Предлагаемые crates",
        "y": 685.1896362304688,
        "page": 65,
        "old": 19,
        "id": "A19-004",
        "source_ids": [
          "A19-004"
        ],
        "anchor": "A19-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "koobique-domain содержит идентификаторы, величины, состояния команд и проверки инвариантов. koobique-wire — кодирование, лимиты и тестовые векторы протокола. Оба по возможности не зависят от Linux и могут иметь no_std-профиль для устройств.",
        "y": 712.2073364257812,
        "page": 65,
        "old": 19,
        "id": "A19-005",
        "source_ids": [
          "A19-005"
        ],
        "anchor": "A19-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "koobique-core реализует реестр, очереди и исполнитель. koobique-storage — транзакции и запросы. koobique-api — мобильный и диагностический интерфейс. koobique-adapter- * — конкретные способы связи. koobique-sim — виртуальные датчики и устройства. Отдельные executables отвечают за privileged-helper, speech-worker и CLI.",
        "y": 57.42815399169922,
        "page": 66,
        "old": 19,
        "id": "A19-006",
        "source_ids": [
          "A19-006"
        ],
        "anchor": "A19-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Структура не должна порождать десятки пустых библиотек: модуль выделяется, когда у него есть собственный контракт или зависимость. Главное — направление зависимостей. Физическая модель не должна знать о конкретной SQL-таблице или библиотеке BLE.",
        "y": 129.83209228515625,
        "page": 66,
        "old": 19,
        "id": "A19-007",
        "source_ids": [
          "A19-007"
        ],
        "anchor": "A19-007",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Владение состоянием",
        "y": 209.62625122070312,
        "page": 66,
        "old": 19,
        "id": "A19-008",
        "source_ids": [
          "A19-008"
        ],
        "anchor": "A19-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Изменяемое состояние исполнительного канала имеет одного логического владельца. Команды этому каналу упорядочиваются. Параллельные датчики могут поступать независимо, но изменение конфигурации имеет номер версии. Операция с устаревшим ожидаемым номером либо отклоняется, либо проходит явно описанную процедуру согласования.",
        "y": 236.64389038085938,
        "page": 66,
        "old": 19,
        "id": "A19-009",
        "source_ids": [
          "A19-009"
        ],
        "anchor": "A19-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Глобальный Mutex<HashMap<...>>, удерживаемый при записи в БД или сетевом ожидании, создаёт скрытую сериализацию всей системы. Tokio отдельно описывает риски удержания блокировок и блокирующих операций в асинхронном исполнении. Это основание для коротких критических секций и явных очередей, а не для запрета всех mutex. [45][82]",
        "y": 325.0228576660156,
        "page": 66,
        "old": 19,
        "id": "A19-010",
        "source_ids": [
          "A19-010"
        ],
        "anchor": "A19-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Ограниченные очереди",
        "y": 420.7920227050781,
        "page": 66,
        "old": 19,
        "id": "A19-011",
        "source_ids": [
          "A19-011"
        ],
        "anchor": "A19-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Каждая очередь имеет ёмкость, класс важности и реакцию на переполнение. Для телеметрии допустимо объединить несколько ещё не обработанных значений одного канала в последнее, если история явно помечается как прореженная. Для команды с обещанным надёжным принятием молчаливая потеря недопустима.",
        "y": 447.80963134765625,
        "page": 66,
        "old": 19,
        "id": "A19-012",
        "source_ids": [
          "A19-012"
        ],
        "anchor": "A19-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Предлагаемые классы: управление и safety-события; актуальное состояние; обычная история; диагностика и большие передачи. Нельзя позволить экспорту графиков занять все буферы и задержать выключение. Однако чрезмерный приоритет одной очереди может голодать остальные, поэтому нужен ограниченный квант обработки и метрика возраста события.",
        "y": 520.2135620117188,
        "page": 66,
        "old": 19,
        "id": "A19-013",
        "source_ids": [
          "A19-013"
        ],
        "anchor": "A19-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Детерминированная логика",
        "y": 615.9827880859375,
        "page": 66,
        "old": 19,
        "id": "A19-014",
        "source_ids": [
          "A19-014"
        ],
        "anchor": "A19-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Чистая функция решения получает snapshot состояния, версию правил и время из внедрённого источника, затем возвращает намерения и объяснение. Она не читает часы и сеть из произвольных мест. Это позволяет воспроизводить ситуацию по журналу и сравнивать новую версию алгоритма без отправки реальных команд.",
        "y": 643.0004272460938,
        "page": 66,
        "old": 19,
        "id": "A19-015",
        "source_ids": [
          "A19-015"
        ],
        "anchor": "A19-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Нельзя обещать полный детерминизм всех физических данных: сеть и измерения приходят с неопределённостью. Но можно сделать детерминированным решение на зафиксированном наборе входов. При воспроизведении история не должна запускать реальные устройства, если режим явно не разрешён.",
        "y": 715.4043579101562,
        "page": 66,
        "old": 19,
        "id": "A19-016",
        "source_ids": [
          "A19-016"
        ],
        "anchor": "A19-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Наблюдаемость и диагностика",
        "y": 56.314456939697266,
        "page": 67,
        "old": 19,
        "id": "A19-017",
        "source_ids": [
          "A19-017"
        ],
        "anchor": "A19-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "У каждой команды есть trace ID и источник: приложение, правило, кнопка, голос. Журнал связывает её с использованными наблюдениями, версией политики и ответом устройства. Метрики включают возраст данных, глубину очереди, время ожидания записи, число переподключений и неизвестные результаты.",
        "y": 83.33208465576172,
        "page": 67,
        "old": 19,
        "id": "A19-018",
        "source_ids": [
          "A19-018"
        ],
        "anchor": "A19-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Логи не являются единственным источником истины. Состояние команды хранится структурированно; текстовый лог помогает человеку. Не следует реконструировать текущую конфигурацию из случайных строк info! после перезапуска.",
        "y": 155.73602294921875,
        "page": 67,
        "old": 19,
        "id": "A19-019",
        "source_ids": [
          "A19-019"
        ],
        "anchor": "A19-019",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Где Rust не обязан быть единственным языком",
        "y": 219.55520629882812,
        "page": 67,
        "old": 19,
        "id": "A19-020",
        "source_ids": [
          "A19-020"
        ],
        "anchor": "A19-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "SQLite, драйверы Linux и зрелые inference-библиотеки могут быть реализованы на других языках. Это не нарушает цель собственного Rust-ядра. Через FFI нужно ограничить время жизни буферов, потоки, отмену и обработку ошибок. Более рискованный ML-runtime лучше изолировать процессом, чем пытаться доказать его безопасность одной обёрткой unsafe.",
        "y": 246.57284545898438,
        "page": 67,
        "old": 19,
        "id": "A19-021",
        "source_ids": [
          "A19-021"
        ],
        "anchor": "A19-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Rustls предоставляет готовую TLS-реализацию и выбор криптографических провайдеров. Axum удобен для HTTP-API и диагностического интерфейса; использование их не превращает Koobique в готовую чужую платформу. [46][47]",
        "y": 334.9517822265625,
        "page": 67,
        "old": 19,
        "id": "A19-022",
        "source_ids": [
          "A19-022"
        ],
        "anchor": "A19-022",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Первый benchmark ядра",
        "y": 407.65057373046875,
        "page": 67,
        "old": 19,
        "id": "A19-023",
        "source_ids": [
          "A19-023"
        ],
        "anchor": "A19-023",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Симулятор подаёт события с управляемой частотой, задержками и сбоями. Одновременно выполняются команды и экспорт истории. Измеряются p50/p95/p99, максимальный возраст события и память. Сначала исправляются неограниченные очереди и блокировки, а уже потом обсуждаются lock-free и низкоуровневые оптимизации.",
        "y": 426.1117858886719,
        "page": 67,
        "old": 19,
        "id": "A19-024",
        "source_ids": [
          "A19-024"
        ],
        "anchor": "A19-024",
        "content_status": "active"
      },
      {
        "kind": "lead",
        "text": "Управление должно оставаться предсказуемым, когда модель загружает GPU, запись истории тормозит, а устройство присылает слишком много данных.",
        "id": "I18-001",
        "old": "I18",
        "source_ids": [
          "I18-001"
        ],
        "anchor": "I18-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Что находится в одном ядре",
        "id": "I18-002",
        "old": "I18",
        "source_ids": [
          "I18-002"
        ],
        "anchor": "I18-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Предлагаем модульный Rust-процесс с явными границами: реестр объектов и возможностей; приём и проверка наблюдений; актуальное состояние; оценщики; планировщик процессов; арбитр полномочий; исполнитель; журнал и API. Это логические модули, не обязательные сетевые микросервисы.",
        "id": "I18-003",
        "old": "I18",
        "source_ids": [
          "I18-003"
        ],
        "anchor": "I18-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Общий код контрактов отделён от Linux, Tokio и базы. В нём находятся типы, версии, единицы, пределы сообщений и преобразования. На устройстве используются только подходящие по памяти части. Семантически значимые поля не передаются как неограниченные строки без схемы. Внутри Rust можно использовать размерностно-типизированные величины, например подход uom. [107] Это снижает риск смешения единиц в коде, но не исправляет неверный внешний дескриптор: данные проверяются на границе.",
        "id": "I18-004",
        "old": "I18",
        "source_ids": [
          "I18-004"
        ],
        "anchor": "I18-004",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I18",
        "caption": "Предлагаемая граница процессов. Быстрый путь не делает синхронных вызовов к AI-worker. Привилегированные операции Linux не доступны модели и не выполняются произвольной shell-командой.",
        "id": "I18-005",
        "old": "I18",
        "source_ids": [
          "I18-005"
        ],
        "anchor": "I18-005",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Очереди и давление нагрузки",
        "id": "I18-006",
        "old": "I18",
        "source_ids": [
          "I18-006"
        ],
        "anchor": "I18-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для каждого источника ограничиваем размер сообщения, частоту и память очереди. Команды и подтверждения не стоят за многомегабайтным аудиофрагментом. Потеря телеметрии фиксируется; потеря критического управляющего события не маскируется таким же механизмом сброса.",
        "id": "I18-007",
        "old": "I18",
        "source_ids": [
          "I18-007"
        ],
        "anchor": "I18-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Полезно хранить актуальное значение как компактный снимок, а историю — отдельным потоком. Подписчик медленного графика может получить последнее состояние и информацию о пропуске. Исполнитель не обязан ждать, пока мобильный клиент просмотрит все промежуточные точки.",
        "id": "I18-008",
        "old": "I18",
        "source_ids": [
          "I18-008"
        ],
        "anchor": "I18-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Одно устройство имеет последовательный поток исполнения команд. Разные независимые ресурсы могут обрабатываться параллельно. Не нужен глобальный mutex вокруг всего «мира», но и повсеместный lock-free не является автоматическим выигрышем. Длительное удержание блокировки и блокирующий код внутри асинхронной задачи способны задерживать другие задачи; это прямо разбирается в документации Tokio. [82]",
        "id": "I18-009",
        "old": "I18",
        "source_ids": [
          "I18-009"
        ],
        "anchor": "I18-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Два разных срока",
        "id": "I18-010",
        "old": "I18",
        "source_ids": [
          "I18-010"
        ],
        "anchor": "I18-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Deadline вычисления определяет, когда результат модели ещё полезен. Срок команды — до какого момента разрешено физическое действие. Завершившийся позже deadline прогноз отправляется в диагностику, не в исполнение. Актуальность проверяется по монотонному времени станции и текущей эпохе управления.",
        "id": "I18-011",
        "old": "I18",
        "source_ids": [
          "I18-011"
        ],
        "anchor": "I18-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Отмена Rust future не гарантирует остановку уже запущенного синхронного native-инференса. Поэтому тяжёлые модели выполняются в отдельном worker-процессе с ограниченным IPC. При зависании процесс можно завершить; состояние ядра и локальное управление остаются целыми.",
        "id": "I18-012",
        "old": "I18",
        "source_ids": [
          "I18-012"
        ],
        "anchor": "I18-012",
        "content_status": "active"
      },
      {
        "id": "N-007",
        "kind": "h2",
        "text": "Отмена ожидания не останавливает физический мир",
        "new": true,
        "source_ids": [
          "N-007"
        ],
        "anchor": "N-007",
        "content_status": "active"
      },
      {
        "id": "N-008",
        "kind": "p",
        "text": "У тайм-аута есть точный предел действия. Tokio может прекратить ожидание future, но длительное вычисление, которое не возвращает управление планировщику, способно превысить заданное время. Уже начавшую работу функцию в spawn_blocking нельзя остановить вызовом abort. Поэтому этот механизм не является аварийным выключателем инференса. [142][143]",
        "new": true,
        "source_ids": [
          "N-008"
        ],
        "anchor": "N-008",
        "content_status": "active"
      },
      {
        "id": "N-009",
        "kind": "p",
        "text": "В нашей архитектуре запрос к вычислительному помощнику получает ID, снимок состояния и дедлайн. Поздний ответ помечается как непригодный к исполнению. При зависании отдельный процесс можно перезапустить; само право на действие при этом не передаётся ни этому процессу, ни его ответу. Устройство отдельно ограничивает свой выход.",
        "new": true,
        "source_ids": [
          "N-009"
        ],
        "anchor": "N-009",
        "content_status": "active"
      },
      {
        "id": "N-010",
        "kind": "p",
        "text": "Пример: модель начала распознавать команду, человек нажал OFF, а распознавание закончилось позже. Отмена запроса полезна для ресурсов, но правильность обеспечивает другое: старая эпоха и версия состояния не проходят проверку исполнителя. Даже если поздний ответ дошёл, он не отменяет кнопку.",
        "new": true,
        "source_ids": [
          "N-010"
        ],
        "anchor": "N-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Выбор ML-runtime",
        "id": "I18-013",
        "old": "I18",
        "source_ids": [
          "I18-013"
        ],
        "anchor": "I18-013",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Кандидат",
            "Полезная роль",
            "Что проверить"
          ],
          [
            "Candle",
            "Rust-инференс и собственные модели",
            "Операторы, checkpoint, backend, память [73]"
          ],
          [
            "Burn",
            "Исследование и обучение на Rust",
            "Зрелость конкретного backend и перенос артефакта [134]"
          ],
          [
            "ort + ONNX Runtime",
            "Использование экспортируемых моделей",
            "Native-зависимость, execution provider, версии операторов [135][137]"
          ],
          [
            "tract",
            "Компактный CPU-инференс на Rust",
            "Поддерживаемое подмножество модели и скорость [136]"
          ],
          [
            "Отдельный исследовательский worker",
            "Быстрая проверка чужой модели без переноса",
            "Не превращать временный Python-стенд в скрытую обязательную зависимость"
          ]
        ],
        "id": "I18-014",
        "old": "I18",
        "source_ids": [
          "I18-014"
        ],
        "anchor": "I18-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Наш собственный производственный код остаётся на Rust. Это не означает, что Linux, драйвер GPU или все математические ядра также написаны на Rust. Безопасность FFI, совместимость ABI и происхождение native-библиотек проверяются отдельно.",
        "id": "I18-015",
        "old": "I18",
        "source_ids": [
          "I18-015"
        ],
        "anchor": "I18-015",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Изоляция ресурсов",
        "id": "I18-016",
        "old": "I18",
        "source_ids": [
          "I18-016"
        ],
        "anchor": "I18-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Linux cgroups v2 позволяет ограничивать и учитывать ресурсы процессов, включая CPU, память и I/O. [132] Это полезный механизм отделения worker от ядра, но не доказательство жёсткого реального времени и не универсальный контроль каждой GPU-операции.",
        "id": "I18-017",
        "old": "I18",
        "source_ids": [
          "I18-017"
        ],
        "anchor": "I18-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "При нехватке памяти первой ограничивается история/инференс по заданной политике, а не исчезает способность обработать кнопку. Для критических физических реакций сохраняется локальный путь устройства. Резерв вычислителя проверяем совместной нагрузкой, а не суммой красивых паспортных цифр.",
        "id": "I18-018",
        "old": "I18",
        "source_ids": [
          "I18-018"
        ],
        "anchor": "I18-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Начальная конструкция: один Rust-core, отдельный AI-worker, отдельный узкий сервис привилегированных операций и существующие системные службы Linux. Дробление на большее число процессов оправдывается измерением или анализом отказов.",
        "id": "I18-019",
        "old": "I18",
        "source_ids": [
          "I18-019"
        ],
        "anchor": "I18-019",
        "content_status": "active"
      },
      {
        "id": "R20-001",
        "kind": "h2",
        "text": "Границы процессов и устройств",
        "source_ids": [
          "R20-001"
        ],
        "anchor": "R20-001",
        "content_status": "active"
      },
      {
        "id": "R20-002",
        "kind": "table",
        "rows": [
          [
            "Процесс",
            "Права и IPC",
            "Сбой"
          ],
          [
            "koobique-core",
            "Непривилегированный; журнал, KQIP/API",
            "Перезапуск; pending-намерения не оживают"
          ],
          [
            "koobique-device-agent",
            "Только нужные BLE/USB; Unix socket с Core",
            "Переподключение адаптера без выдачи root Core"
          ],
          [
            "koobique-admin-helper",
            "Узкие сетевые/установочные операции; проверяемый IPC",
            "Запрос отклоняется; нет shell из пользовательского текста"
          ],
          [
            "koobique-speech-worker",
            "Аудио/модель по профилю; без GPIO и секретов",
            "Тайм-аут не останавливает кнопку и Core"
          ],
          [
            "koobique-model-worker",
            "Лимиты CPU/RAM; снимок → предложение",
            "Кандидат отбрасывается, работает базовый автомат"
          ]
        ],
        "source_ids": [
          "R20-002"
        ],
        "anchor": "R20-002",
        "content_status": "active"
      },
      {
        "id": "R20-003",
        "kind": "p",
        "text": "Внутри Core остаются протокольные адаптеры без привилегированного доступа. Работа с USB/BT вынесена в device-agent; так устраняется двойное описание одной границы. Если конкретный E0 не использует BLE на Hub, этот процесс просто не запускается.",
        "source_ids": [
          "R20-003"
        ],
        "anchor": "R20-003",
        "content_status": "active"
      },
      {
        "id": "R20-004",
        "kind": "p",
        "text": "Критические задачи Rust регистрируются у супервизора: результат JoinHandle нельзя игнорировать. Для E0 предлагается fail-stop Core при потере владельца команд и перезапуск systemd; выбор panic=abort либо контролируемого unwind фиксируется сборкой и тестом. Синхронная SQLite работает на выделенном потоке; длительное вычисление не попадает в сетевой executor. [162]",
        "source_ids": [
          "R20-004"
        ],
        "anchor": "R20-004",
        "content_status": "active"
      }
    ],
    "origins": [
      "A19",
      "I18"
    ],
    "question": "Почему быстрый сервер — это прежде всего ограниченные очереди?",
    "intro": "В асинхронной системе легко создать впечатление бесконечного параллелизма. На самом деле процессор, память и накопитель конечны. Если телеметрия, голос и команды занимают одну неограниченную очередь, задержка появится именно в момент, когда система больше всего нужна.\n\nРазберём, какие данные хранятся в памяти, кто ими владеет, где нужна последовательность и какие вычисления выносятся в отдельный процесс. Быстрый путь команды будет коротким, но не будет обходить проверку прав и состояния.",
    "terms": [
      "ABI — двоичный договор между скомпилированными компонентами",
      "IPC — обмен между процессами"
    ],
    "id": "ch-20",
    "slug": "chapter-20"
  },
  {
    "number": 21,
    "title": "KQIP: свой язык устройств",
    "group": "Протокол",
    "units": [
      {
        "kind": "lead",
        "text": "Протокол описывает смысл, пределы и восстановление. Двоичная упаковка — только один из его слоёв.",
        "y": 137.3555450439453,
        "page": 68,
        "old": 20,
        "id": "A20-001",
        "source_ids": [
          "A20-001"
        ],
        "anchor": "A20-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "KQIP — Koobique Interface Protocol, собственный прикладной язык Koobique. Текущий проектный профиль — E0 поверх кадра 0.2; совместимых реализаций пока нет. Для первого узла выбираются TCP, TLS 1.3 и ограниченный CBOR. Прежний 27-байтовый Hello 0.1 оставлен в главе 22 только как учебный разбор, не как рабочий handshake.",
        "y": 188.46142578125,
        "page": 68,
        "old": 20,
        "id": "A20-002",
        "source_ids": [
          "A20-002"
        ],
        "anchor": "A20-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Почему не собственный UDP сразу",
        "y": 268.2555847167969,
        "page": 68,
        "old": 20,
        "id": "A20-003",
        "source_ids": [
          "A20-003"
        ],
        "anchor": "A20-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "TCP снимает с первой реализации часть задач доставки и контроля потока. Это не означает его оптимальность для каждого будущего носимого датчика. BLE, QUIC или UDP-профиль могут появиться позднее при конкретной необходимости. Прикладные идентификаторы, команды, ошибки и синхронизация должны переноситься между транспортами без изменения смысла.",
        "y": 295.27325439453125,
        "page": 68,
        "old": 20,
        "id": "A20-004",
        "source_ids": [
          "A20-004"
        ],
        "anchor": "A20-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "TLS обеспечивает защищённый канал; права доступа и допустимость команды проверяются отдельно. CBOR удобен компактным представлением и возможностью ограниченной реализации. minicbor поддерживает сценарии без стандартной библиотеки. Использование этих механизмов не отменяет собственную спецификацию. [47][48] [49][50]",
        "y": 383.6521911621094,
        "page": 68,
        "old": 20,
        "id": "A20-005",
        "source_ids": [
          "A20-005"
        ],
        "anchor": "A20-005",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Кадр 0.2 и профиль E0",
        "y": 56.314456939697266,
        "page": 69,
        "old": 20,
        "id": "A20-006",
        "source_ids": [
          "A20-006"
        ],
        "anchor": "A20-006",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A26",
        "caption": "Предлагаемая структура сетевого кадра. Версия формата фиксируется независимо от памяти Rust-структур.",
        "y": 91.10079193115234,
        "page": 69,
        "old": 20,
        "id": "A20-007",
        "source_ids": [
          "A20-007"
        ],
        "anchor": "A20-007",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Поле",
            "Размер",
            "E0-R2"
          ],
          [
            "magic",
            "4 байта",
            "KQB1 — постоянная сигнатура семейства"
          ],
          [
            "major / minor",
            "1 + 1",
            "0 / 2; другой профиль до согласования запрещён"
          ],
          [
            "message_type",
            "2",
            "Реестр типов E0-R2"
          ],
          [
            "flags",
            "2",
            "0; неизвестные биты запрещены"
          ],
          [
            "header_len",
            "2",
            "24, без расширения в E0"
          ],
          [
            "body_len",
            "4",
            "Не более 4096 байт, затем проверка лимита типа"
          ],
          [
            "session_seq",
            "8",
            "С 1, отдельно в каждом направлении нового TLS-соединения"
          ]
        ],
        "y": 455.24,
        "page": 69,
        "old": 20,
        "id": "A20-008",
        "source_ids": [
          "A20-008"
        ],
        "anchor": "A20-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Многобайтовые числа заголовка передаются в network byte order. Тело не является memcpy Rust-структуры: выравнивание, endian и enum representation не входят в переносимый контракт. Ключи CBOR имеют закреплённые номера и смысл.",
        "y": 57.42815399169922,
        "page": 70,
        "old": 20,
        "id": "A20-009",
        "source_ids": [
          "A20-009"
        ],
        "anchor": "A20-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В исправленном проектном профиле E0 / KQIP 0.2, revision R3-draft тело ограничено 4096 байтами, вложенность — 8 уровнями, строка — 128 байтами UTF-8, byte string — 1024 байтами, массив — 256 элементами, карта — 32 полями; конкретное поле может иметь меньший предел. Запрещены повторные ключи, indefinite-length, теги, floats, NaN и Infinity. Измерения используют нормализованную целую мантиссу и десятичный показатель. Канонические значения находятся в contracts/registry.json; таблицы и схемы генерируются из него. Bulk/OTA при необходимости использует отдельный ограниченный канал: приоритет очереди не обгоняет байты, уже переданные TCP. [48]",
        "y": 113.85709381103516,
        "page": 70,
        "old": 20,
        "id": "A20-010",
        "source_ids": [
          "A20-010"
        ],
        "anchor": "A20-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Сообщения и совместимость",
        "y": 225.6012725830078,
        "page": 70,
        "old": 20,
        "id": "A20-011",
        "source_ids": [
          "A20-011"
        ],
        "anchor": "A20-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Канонический набор KQIP 0.2: Hello, Ready, Describe, Snapshot, Observation, Event, Command, CommandResult, StatusQuery, Resync, Ping, Pong, TelemetryAck и Error. Имена HelloAck, CommandStatusQuery, SyncRequest и SyncReply не являются дополнительными типами этого профиля. Успешный TLS ещё не разрешает команды: проверяются связанная с ключом идентичность, роль и установка, профиль, дескриптор, snapshot и пригодная оценка часов. Полная машина соединения и её интероперабельность остаются отдельной приёмкой.",
        "y": 252.618896484375,
        "page": 70,
        "old": 20,
        "id": "A20-012",
        "source_ids": [
          "A20-012"
        ],
        "anchor": "A20-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В спецификации нужны примеры корректных байтов, заведомо неправильные пакеты и ожидаемые ошибки. Один набор golden vectors проверяет реализации KQIP на Core и MCU и любую дополнительную сторону, если она явно получает роль участника этого протокола. Мобильный клиент по умолчанию обращается к отдельному Client API. Версия устройства, версии capability и версия кадра — отдельные величины.",
        "y": 340.9978332519531,
        "page": 70,
        "old": 20,
        "id": "A20-013",
        "source_ids": [
          "A20-013"
        ],
        "anchor": "A20-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Идентификаторы и повторы",
        "y": 420.7920227050781,
        "page": 70,
        "old": 20,
        "id": "A20-014",
        "source_ids": [
          "A20-014"
        ],
        "anchor": "A20-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Узел имеет постоянный ID, каждая загрузка — boot_id, каждое наблюдение — номер последовательности. Команда имеет 128-битный command_id, выбранный инициатором и сохраняемый при повторе. Session_seq не заменяет command_id: после переподключения прикладной смысл команды остаётся прежним.",
        "y": 447.80963134765625,
        "page": 70,
        "old": 20,
        "id": "A20-015",
        "source_ids": [
          "A20-015"
        ],
        "anchor": "A20-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "set(on) удобнее toggle, но одной идемпотентности значения недостаточно. Между двумя доставками пользователь мог нажать OFF. Повтор прежнего ID тогда возвращает сохранённый результат без нового действия; при отсутствии записи срабатывает защита от старого execution_seq. Для дозирования, шага мотора и других необратимых операций E0 не выдаёт допуска. Повтор toggle инвертировал бы уже изменённый выход, тогда как SET задаёт требуемое значение явно. Сбой между физическим эффектом и записью результата всё равно оставляет неопределённость: гарантия exactly-once для физического мира не заявляется.",
        "y": 520.2135620117188,
        "page": 70,
        "old": 20,
        "id": "A20-016",
        "source_ids": [
          "A20-016"
        ],
        "anchor": "A20-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Когда команда считается принятой",
        "y": 56.314456939697266,
        "page": 71,
        "old": 20,
        "id": "A20-017",
        "source_ids": [
          "A20-017"
        ],
        "anchor": "A20-017",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A27",
        "caption": "Пять раздельных записей: намерение, попытка, исполнение, текущее состояние и свидетельство эффекта. События попадают в приложение через Core; стрелки показывают логическую последовательность. Поздний ACK не возвращает старый уровень выхода.",
        "y": 91.10079193115234,
        "page": 71,
        "old": 20,
        "id": "A20-018",
        "source_ids": [
          "A20-018"
        ],
        "anchor": "A20-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "ACCEPTED означает транзакционную запись намерения в Core. DISPATCHED — попытку передачи, не доставку. RECEIVED относится к получению MCU, APPLIED — к историческому результату операции. Текущее состояние хранится отдельным Snapshot. VERIFIED допустимо лишь как состояние отдельного EffectEvidence; оно не делает исторический ON текущим после более нового OFF. UNKNOWN означает недостаток знания и может уточняться позднее.",
        "y": 517.1915893554688,
        "page": 71,
        "old": 20,
        "id": "A20-019",
        "source_ids": [
          "A20-019"
        ],
        "anchor": "A20-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первое получение после not_after_device_us не разрешает действие. Повтор уже выполненной команды возвращает исторический результат, даже если срок нового исполнения истёк: таймер не стирает прошлое. StatusQuery только читает историю и не может снова включить выход. По окончании срока новая попытка — новый ID, новое решение и подтверждённый пользовательский смысл, а не скрытое продление старого намерения.",
        "y": 605.570556640625,
        "page": 71,
        "old": 20,
        "id": "A20-020",
        "source_ids": [
          "A20-020"
        ],
        "anchor": "A20-020",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Порядок, snapshots и большие потоки",
        "y": 56.314456939697266,
        "page": 72,
        "old": 20,
        "id": "A20-021",
        "source_ids": [
          "A20-021"
        ],
        "anchor": "A20-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Snapshot содержит boot_id, controller_fence, output_revision, manual_revision, выход и ручной режим. Старый ACK не обновляет плитку. История с меткой backfill не становится живым триггером. OTA использует отдельный bulk-канал; его пауза при управляющей нагрузке не заменяет испытания конкуренции за радио и CPU. Для профиля с OTA образ передаётся ограниченными фрагментами с проверяемыми offset, длиной и принадлежностью одному подписанному образу. Размер фрагмента ограничивается до выделения буфера; бесконечный CBOR-массив или всё содержимое образа в одном сообщении не допускаются. Чанки и восстановление передачи остаются отдельной приёмкой E1.",
        "y": 83.33208465576172,
        "page": 72,
        "old": 20,
        "id": "A20-022",
        "source_ids": [
          "A20-022"
        ],
        "anchor": "A20-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "TCP гарантирует порядок байтов в одном соединении, но не создаёт общий порядок между разными устройствами. Станция не должна делать вывод, что датчик A измерил раньше B только потому, что его пакет первым попал в сокет.",
        "y": 171.71102905273438,
        "page": 72,
        "old": 20,
        "id": "A20-023",
        "source_ids": [
          "A20-023"
        ],
        "anchor": "A20-023",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Безопасная обработка",
        "y": 235.53021240234375,
        "page": 72,
        "old": 20,
        "id": "A20-024",
        "source_ids": [
          "A20-024"
        ],
        "anchor": "A20-024",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Длины проверяются до выделения буфера. Handshake ограничен по времени и числу параллельных попыток. Неизвестный peer не получает бесконечный ресурс парсера. CBOR-парсер проверяется fuzz-тестами; журнал не печатает неограниченную строку из пакета.",
        "y": 262.5478515625,
        "page": 72,
        "old": 20,
        "id": "A20-025",
        "source_ids": [
          "A20-025"
        ],
        "anchor": "A20-025",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "TLS 0-RTT для управляющих действий в первом профиле отключается из-за риска повторного воспроизведения. Дополнительный CRC не является заменой аутентификации. Перед изменением версии сохраняются тесты старого поведения и правила отказа от несовместимого соединения. [50]",
        "y": 334.9517822265625,
        "page": 72,
        "old": 20,
        "id": "A20-026",
        "source_ids": [
          "A20-026"
        ],
        "anchor": "A20-026",
        "content_status": "active"
      },
      {
        "id": "R21-001",
        "kind": "h2",
        "text": "Реестр E0 и жизненный цикл соединения",
        "source_ids": [
          "R21-001"
        ],
        "anchor": "R21-001",
        "content_status": "active"
      },
      {
        "id": "R21-002",
        "kind": "table",
        "rows": [
          [
            "Тип",
            "Название",
            "Назначение"
          ],
          [
            "0x0001 / 0x0002",
            "Hello / Ready",
            "Версия, роль, ID, boot, лимиты; затем допуск"
          ],
          [
            "0x0010 / 0x0011",
            "Describe / Snapshot",
            "Описание и состояние, включая ручной режим"
          ],
          [
            "0x0020 / 0x0021",
            "Observation / Event",
            "Отсчёт и дискретное событие; live отделён от backfill"
          ],
          [
            "0x0030 / 0x0031",
            "Command / CommandResult",
            "Ограниченная команда и исторический результат"
          ],
          [
            "0x0032 / 0x0033",
            "StatusQuery / Resync",
            "Чтение результата и запрос согласованного состояния"
          ],
          [
            "0x0040 / 0x0041",
            "Ping / Pong",
            "Жизнь канала и четыре метки времени"
          ],
          [
            "0x0042",
            "TelemetryAck",
            "Подтверждённая граница хранения и кредит"
          ],
          [
            "0x00FF",
            "Error",
            "Код, ссылка на запрос; без секретов"
          ]
        ],
        "source_ids": [
          "R21-002"
        ],
        "anchor": "R21-002",
        "content_status": "active"
      },
      {
        "id": "R21-003",
        "kind": "p",
        "text": "TLS → Hello в обе стороны → проверка роли/версии/лимитов → Ready → запрос Snapshot/Describe → синхронизация → ACTIVE. Command до ACTIVE отклоняется. После смены boot или descriptor Core снимает допуск автоматизации до повторной проверки. Несовместимость 0.1 и 0.2 не маскируется minor-обновлением: обе стороны E0 обязаны явно поддерживать 0.2.",
        "source_ids": [
          "R21-003"
        ],
        "anchor": "R21-003",
        "content_status": "active"
      },
      {
        "id": "R21-004",
        "kind": "p",
        "text": "session_seq — порядковый номер кадра в одном направлении одной TLS-сессии. На reconnect начинается с 1; он обнаруживает ошибку потока, а не служит защитой от повторного физического действия. Для связи ответов используется command_id либо поле reply_to. Magic KQB1 оставлен как историческая сигнатура; цифра не объявляется номером текущего профиля.",
        "source_ids": [
          "R21-004"
        ],
        "anchor": "R21-004",
        "content_status": "active"
      },
      {
        "id": "R21-005",
        "kind": "h2",
        "text": "Строгий CBOR без ловушек сравнения",
        "source_ids": [
          "R21-005"
        ],
        "anchor": "R21-005",
        "content_status": "active"
      },
      {
        "id": "R21-006",
        "kind": "p",
        "text": "E0 принимает только детерминированное кодирование: кратчайшие целые, упорядоченные числовые ключи, конечные длины контейнеров. Повтор ключа, float любого размера, NaN/Infinity, теги, indefinite-length и неизвестный ключ 1…127 отклоняются. Поля 128…255 могут быть только документированными некритичными расширениями; права, сроки, версии и запреты нельзя поместить туда. Новое обязательное поле требует нового профиля. Подписанные билеты COSE имеют отдельную схему контейнера и не отменяют ограничения обычного сообщения. [48][149]",
        "source_ids": [
          "R21-006"
        ],
        "anchor": "R21-006",
        "content_status": "active"
      },
      {
        "id": "R21-007",
        "kind": "p",
        "text": "Общие пределы: тело 4096 байт, вложенность 8, map до 32 элементов, array до 256 элементов, строка до 128 байт, bstr до 1024 байт, если тип не задаёт меньший предел. ID — ровно 16 байт; counters — uint64 с проверкой переполнения. Длины и типы проверяются до резервирования памяти. Декодер не должен принимать true как целое 1 или выбирать «последнее» значение повторного ключа.",
        "source_ids": [
          "R21-007"
        ],
        "anchor": "R21-007",
        "content_status": "active"
      },
      {
        "id": "R21-008",
        "kind": "h2",
        "text": "Два канала и ограниченные очереди",
        "source_ids": [
          "R21-008"
        ],
        "anchor": "R21-008",
        "content_status": "active"
      },
      {
        "id": "R21-009",
        "kind": "p",
        "text": "В E0 управление и небольшая текущая телеметрия используют основное TLS/TCP-соединение. OTA и длинная история — отдельное TLS-соединение с той же проверенной идентичностью и ограничением скорости. Приоритет в очереди не позволяет обогнать байты, уже переданные одному TCP-потоку. Разделение соединений также не отменяет конкуренции за Wi-Fi, flash и CPU; обновление не проходит допуск без измерений задержки кнопки и команд под нагрузкой.",
        "source_ids": [
          "R21-009"
        ],
        "anchor": "R21-009",
        "content_status": "active"
      },
      {
        "id": "R21-010",
        "kind": "p",
        "text": "Начальные сетевые лимиты: handshake до 5 с, до 4 незавершённых handshake на Hub суммарно для E0, один управляющий и один bulk-сеанс на узел, Ping раз в 10 с и разрыв после трёх отсутствующих ответов. Это проектные значения с возможностью измеренного изменения. TCP_USER_TIMEOUT применяется только там, где его поддерживает стек. Потеря heartbeat сообщает о канале, но не доказывает пустоту комнаты.",
        "source_ids": [
          "R21-010"
        ],
        "anchor": "R21-010",
        "content_status": "active"
      },
      {
        "id": "R21-011",
        "kind": "p",
        "text": "TelemetryAck содержит boot_id, channel_id, durable_through_seq и credit. Курсор продвигается только по непрерывно сохранённому диапазону либо после явно записанного Gap. «Получено в RAM» и «сохранено» не смешиваются. Узел не освобождает надёжный буфер по подтверждению только сетевого приёма. Backfill остаётся историей даже при только что установленном соединении.",
        "source_ids": [
          "R21-011"
        ],
        "anchor": "R21-011",
        "content_status": "active"
      },
      {
        "id": "R21-012",
        "kind": "h2",
        "text": "Почему собственный протокол — осознанная стоимость",
        "source_ids": [
          "R21-012"
        ],
        "anchor": "R21-012",
        "content_status": "active"
      },
      {
        "id": "R21-013",
        "kind": "p",
        "text": "CoAP + OSCORE/EDHOC, Matter и MQTT-профили остаются альтернативами транспорта и интеграции. SenML подходит для обмена простыми сенсорными величинами. KQIP выбран ради собственного учебного проекта и единого договора исполнения, не потому, что доказан быстрее готовых протоколов. При экспорте в SenML нельзя терять boot, интервал, калибровку и размещение: невыразимые поля идут в сопроводительные метаданные. [169][170][168]",
        "source_ids": [
          "R21-013"
        ],
        "anchor": "R21-013",
        "content_status": "active"
      }
    ],
    "origins": [
      "A20"
    ],
    "question": "Чем собственный протокол отличается от произвольного JSON?",
    "intro": "Протокол — это договор не только о байтах, но и о поведении. Что означает подтверждение? Можно ли повторить команду? Когда сообщение считается старым? Что делать с новой версией поля?\n\nKQIP задаёт эти правила для наших устройств. TCP доставляет поток байтов, TLS защищает соединение, а собственный слой определяет границы сообщений и их смысл. Такое разделение позволяет сосредоточиться на поведении экосистемы, не изобретая криптографию.",
    "id": "ch-21",
    "slug": "chapter-21"
  },
  {
    "number": 22,
    "title": "Сообщение под увеличением",
    "group": "Протокол",
    "units": [
      {
        "kind": "lead",
        "text": "Здесь собран текущий проектный E0 и отмечены учебные эскизы. Проверенные байты не означают проверенный TLS, MCU или приложение.",
        "y": 168.675537109375,
        "page": 130,
        "old": 39,
        "id": "A39-001",
        "source_ids": [
          "A39-001"
        ],
        "anchor": "A39-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Наблюдение в читаемом представлении",
        "y": 218.667724609375,
        "page": 130,
        "old": 39,
        "id": "A39-002",
        "source_ids": [
          "A39-002"
        ],
        "anchor": "A39-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Ниже приведён диагностический JSON с числовыми ключами закреплённого профиля KQIP 0.2. В JSON ключи записаны строками, а бинарные ID — шестнадцатеричным текстом; на проводе это целочисленные ключи CBOR и byte strings. Соответствие ключей типам и полям задаёт contracts/generated/SPEC_RU.md. JSON служит представлением пакета для чтения, а не альтернативным форматом передачи. UUID, идентификатор загрузки и последовательность не взаимозаменяемы.",
        "y": 245.68536376953125,
        "page": 130,
        "old": 39,
        "id": "A39-003",
        "source_ids": [
          "A39-003"
        ],
        "anchor": "A39-003",
        "content_status": "active"
      },
      {
        "kind": "code",
        "text": "{\n  \"1\": \"11111111111111111111111111111111\",\n  \"2\": 1,\n  \"3\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\n  \"4\": 1042,\n  \"5\": 120000000,\n  \"6\": 120010000,\n  \"7\": 234,\n  \"8\": -1,\n  \"9\": 1,\n  \"10\": 0,\n  \"11\": 0,\n  \"12\": 3,\n  \"13\": 2,\n  \"14\": 1,\n  \"15\": 0\n}",
        "y": 332.9233703613281,
        "page": 130,
        "old": 39,
        "id": "A39-004",
        "source_ids": [
          "A39-004"
        ],
        "anchor": "A39-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В Observation ключи 1 и 3 обозначают ID узла и загрузки: каждый показан 32 шестнадцатеричными знаками, то есть 16 байтами. Ключ 2 выбирает канал 1, который descriptor определяет как sensor_temperature. Ключи 7 и 8 задают мантиссу 234 и показатель −1; ключ 9 выбирает единицу 1 — degC. Поэтому измерение равно 23,4 °C. Десятичная пара нормализуется: лишний ноль в мантиссе удаляется, пока показатель можно увеличить в разрешённом диапазоне; ноль имеет показатель 0. На проводе float и NaN в E0 не используются. Остальные обязательные поля, включая delivery_kind, расшифрованы в сгенерированной спецификации профиля.",
        "y": 516.7896728515625,
        "page": 130,
        "old": 39,
        "id": "A39-005",
        "source_ids": [
          "A39-005"
        ],
        "anchor": "A39-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "sample_start_us и sample_end_us задают ограничивающий интервал получения отсчёта в шкале boot_id. Для SHT45 здесь 10 мс — консервативная граница от команды до чтения, а не паспортное интегрирование 15 мс. Сам датчик реагирует на изменение температуры значительно медленнее длительности преобразования. Эти два времени хранятся и используются по-разному. [157]",
        "y": 605.1686401367188,
        "page": 130,
        "old": 39,
        "id": "A39-006",
        "source_ids": [
          "A39-006"
        ],
        "anchor": "A39-006",
        "content_status": "active"
      },
      {
        "id": "N-011",
        "kind": "h2",
        "text": "Одна запись на трёх уровнях",
        "new": true,
        "source_ids": [
          "N-011"
        ],
        "anchor": "N-011",
        "content_status": "active"
      },
      {
        "id": "N-012",
        "kind": "p",
        "text": "Возьмём температуру 23,4 °C. На плате драйвер знает режим преобразования, локальное время и статус сенсора. Кодек передаёт эти сведения вместе с идентификаторами и числом. На станции запись дополняется временем приёма, оценкой преобразования часов, зарегистрированным размещением и ссылкой на калибровку. Часть метаданных может храниться в дескрипторе с устойчивой версией, а не повторяться в каждом коротком пакете.",
        "new": true,
        "source_ids": [
          "N-012"
        ],
        "anchor": "N-012",
        "content_status": "active"
      },
      {
        "id": "N-013",
        "kind": "p",
        "text": "Это разделение экономит сеть без потери смысла. Но оно требует правила: сервер не подставляет нынешнюю ревизию размещения в старую запись, если та была измерена в другом месте. Неизвестная ревизия остаётся неизвестной или требует повторного запроса описания. Сокращение пакета не даёт права придумать отсутствующую метаинформацию.",
        "new": true,
        "source_ids": [
          "N-013"
        ],
        "anchor": "N-013",
        "content_status": "active"
      },
      {
        "id": "N-014",
        "kind": "table",
        "rows": [
          [
            "Уровень",
            "Что хранится",
            "Что не обещается"
          ],
          [
            "Драйвер и узел",
            "Отсчёт, локальное время, режим, статус",
            "Абсолютно точное время станции"
          ],
          [
            "Кадр KQIP",
            "Версия профиля, ID, поля и ссылки по схеме",
            "Полный граф комнаты в каждом пакете"
          ],
          [
            "Логическое наблюдение",
            "Объект, единица, интервал, качество, происхождение",
            "Что поздно полученная запись свежая"
          ]
        ],
        "new": true,
        "source_ids": [
          "N-014"
        ],
        "anchor": "N-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Capability и команда",
        "y": 684.9628295898438,
        "page": 130,
        "old": 39,
        "id": "A39-007",
        "source_ids": [
          "A39-007"
        ],
        "anchor": "A39-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Описание канала сообщает тип данных, единицу, операции, пределы и способ подтверждения. relay.switch принимает boolean; датчик lux не становится записываемым только потому, что имеет поле value. Ограничения в описании проверяются также на устройстве: клиентская валидация приложения не является защитой.",
        "y": 711.9804077148438,
        "page": 130,
        "old": 39,
        "id": "A39-008",
        "source_ids": [
          "A39-008"
        ],
        "anchor": "A39-008",
        "content_status": "active"
      },
      {
        "kind": "code",
        "text": "{\n  \"1\": \"7c8b7b8e73104f21842a5d70a9b4b10a\",\n  \"2\": \"22222222222222222222222222222222\",\n  \"3\": 1,\n  \"4\": \"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\n  \"5\": 4,\n  \"6\": 19,\n  \"7\": 38,\n  \"8\": 7,\n  \"9\": 6969000,\n  \"10\": 1,\n  \"11\": true,\n  \"12\": 0\n}",
        "y": 69.42756652832031,
        "page": 131,
        "old": 39,
        "id": "A39-009",
        "source_ids": [
          "A39-009"
        ],
        "anchor": "A39-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В E0 предусловия обязательны: boot_id исполнителя, controller_fence, output_revision и manual_revision. Отсутствующее поле не разрешает безусловную установку. Сначала проверяются действующая авторизация и привязка к boot/fence. Для уже известного command_id в этой области кэш результата проверяется до сравнения изменяемых ревизий: совпадающее тело возвращает прежний результат без GPIO. Иначе потерянный ACK превратил бы успешный повтор в ложный конфликт. Новая команда проходит все текущие предусловия.",
        "y": 203.493896484375,
        "page": 131,
        "old": 39,
        "id": "A39-010",
        "source_ids": [
          "A39-010"
        ],
        "anchor": "A39-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Срок нового исполнения фиксируется один раз. Core сохраняет своё время истечения в текущей загрузке и консервативно переводит его в not_after_device_us по интервальной оценке часов. Это поле не увеличивается при повторе. MCU проверяет собственные часы непосредственно перед изменением GPIO; он не запускает новый полный TTL с момента получения.",
        "y": 275.8978576660156,
        "page": 131,
        "old": 39,
        "id": "A39-011",
        "source_ids": [
          "A39-011"
        ],
        "anchor": "A39-011",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Исторический малый вектор 0.1: только устройство кадра",
        "y": 371.6670227050781,
        "page": 131,
        "old": 39,
        "id": "A39-012",
        "source_ids": [
          "A39-012"
        ],
        "anchor": "A39-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для объяснения длины сохранён прежний искусственный Hello 0.1: тип 0x0001 и тело {1: 1}, то есть a1 01 01. Эти 27 байт полезны как задача на декодирование; профиль E0 их отвергает по версии. Рабочие поля Hello и новые эталоны определены ниже и в комплекте protocol_r2. [48]",
        "y": 398.68463134765625,
        "page": 131,
        "old": 39,
        "id": "A39-013",
        "source_ids": [
          "A39-013"
        ],
        "anchor": "A39-013",
        "content_status": "active"
      },
      {
        "kind": "code",
        "text": "4b 51 42 31  00 01  00 01  00 00  00 18\n00 00 00 03  00 00 00 00 00 00 00 01\na1 01 01",
        "y": 485.92266845703125,
        "page": 131,
        "old": 39,
        "id": "A39-014",
        "source_ids": [
          "A39-014"
        ],
        "anchor": "A39-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первые четыре байта — ASCII KQB1. Далее идут major=0, minor=1, тип=1, flags=0, header_len=24, body_len=3, session_seq=1. Итого 27 байт прикладного кадра, не считая TLS/TCP/IP. При реальной передаче заголовок может прийти частями; один read не обязан вернуть целое сообщение.",
        "y": 545.2890014648438,
        "page": 131,
        "old": 39,
        "id": "A39-015",
        "source_ids": [
          "A39-015"
        ],
        "anchor": "A39-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Golden vectors сохраняются как независимые файлы: входные байты, ожидаемая структура и ожидаемая ошибка. Для несовместимой major-версии, повторяющегося ключа и слишком большого body_len должны существовать отрицательные примеры. Криптографический handshake не подменяется этим тестом: он проверяется отдельно.",
        "y": 617.6929321289062,
        "page": 131,
        "old": 39,
        "id": "A39-016",
        "source_ids": [
          "A39-016"
        ],
        "anchor": "A39-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Реестр ошибок",
        "y": 56.314456939697266,
        "page": 132,
        "old": 39,
        "id": "A39-017",
        "source_ids": [
          "A39-017"
        ],
        "anchor": "A39-017",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Класс",
            "Коды KQIP 0.2 / R3-draft",
            "Поведение клиента"
          ],
          [
            "Формат",
            "INVALID_FRAME (13), INVALID_VALUE (6)",
            "Повреждённый запрос не повторяется бесконечно"
          ],
          [
            "Совместимость",
            "PROFILE_MISMATCH (12), SYNC_REQUIRED (14)",
            "Нужен совместимый профиль или синхронизация"
          ],
          [
            "Доступ и привязка",
            "UNAUTHORIZED (1), BOOT_MISMATCH (2), FENCE_MISMATCH (3)",
            "Проверить полномочия и привязку; не отключать TLS"
          ],
          [
            "Команда",
            "ID_CONFLICT (4), STATE_CONFLICT (8), EXPIRED (9)",
            "Не менять тело прежнего ID; новое намерение требует текущих условий"
          ],
          [
            "Ручной режим и ресурсы",
            "MANUAL_HOLD (7), RESOURCE_LIMIT (10), STORAGE_FAILURE (15)",
            "Показать ограничение; не обещать исполнение"
          ],
          [
            "История",
            "RESULT_EVICTED (5), UNKNOWN_COMMAND (11)",
            "Неизвестный результат не доказывает отсутствие действия"
          ],
          [
            "Успех",
            "OK (0)",
            "Успешный известный результат; не UNKNOWN"
          ]
        ],
        "y": 85.43,
        "page": 132,
        "old": 39,
        "id": "A39-018",
        "source_ids": [
          "A39-018"
        ],
        "anchor": "A39-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Человекочитаемое сообщение не служит машинным кодом. Локализация русского и английского текста не должна ломать обработку ошибок. Диагностические детали не раскрывают секреты, существование чужих устройств или произвольные внутренние пути.",
        "y": 365.7362060546875,
        "page": 132,
        "old": 39,
        "id": "A39-019",
        "source_ids": [
          "A39-019"
        ],
        "anchor": "A39-019",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Планировщик узла и бюджет памяти",
        "y": 445.5303955078125,
        "page": 132,
        "old": 39,
        "id": "A39-020",
        "source_ids": [
          "A39-020"
        ],
        "anchor": "A39-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "При выборе ESP-IDF или no_std фиксируются реальные области памяти: сеть и криптография, стеки задач, очереди, драйверные буферы, OTA и запас на одновременные операции. Максимальный размер прикладного кадра 4096 байт не означает, что TLS-соединению нужно всего 4096 байт RAM.",
        "y": 472.54803466796875,
        "page": 132,
        "old": 39,
        "id": "A39-021",
        "source_ids": [
          "A39-021"
        ],
        "anchor": "A39-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Как учебный расчёт можно выделить очередь из 32 нормализованных событий по 96 байт: это 3072 байта полезных записей, ещё без служебных полей контейнера. Очередь 16 команд по 128 байт даёт 2048 байт. Размеры должны проверяться фактическим типом и сборкой. Общая RAM определяется пиковым сценарием BLE onboarding + Wi-Fi + TLS, а не спокойным измерением температуры.",
        "y": 544.9519653320312,
        "page": 132,
        "old": 39,
        "id": "A39-022",
        "source_ids": [
          "A39-022"
        ],
        "anchor": "A39-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "При переполнении некритичной телеметрии допустимы объединение последнего значения и явный счётчик пропусков. Команда не теряется молча: RESOURCE_LIMIT возвращается до действия либо запрос помещается в надёжную очередь с заданным пределом. BUSY не является отдельным кодом исправленного реестра. События кнопки имеют защищённый бюджет очереди, чтобы поток UART радара их не вытеснил.",
        "y": 633.3308715820312,
        "page": 132,
        "old": 39,
        "id": "A39-023",
        "source_ids": [
          "A39-023"
        ],
        "anchor": "A39-023",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Временная дисциплина",
        "y": 56.314456939697266,
        "page": 133,
        "old": 39,
        "id": "A39-024",
        "source_ids": [
          "A39-024"
        ],
        "anchor": "A39-024",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сроки на Linux привязаны к CLOCK_BOOTTIME и host_boot_id. Rust Instant либо таймер Tokio не подменяют этот договор автоматически. После resume Core закрывает прежние рабочие сессии и пересогласует время; после reboot краткоживущий outbox не переисполняется. История фактически отправленных команд остаётся UNKNOWN до свидетельства, а не объявляется «никогда не выполненной». [145]",
        "y": 83.33208465576172,
        "page": 133,
        "old": 39,
        "id": "A39-025",
        "source_ids": [
          "A39-025"
        ],
        "anchor": "A39-025",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Clock sync полезен, но не делает разные часы одинаковыми. У записи сохраняется погрешность преобразования; при её росте уменьшается уверенность в порядке событий. Для первой проверки задержки используется полный RTT на одном клиенте. Физическое включение можно дополнительно измерить фотодатчиком и логическим анализатором с общей временной базой.",
        "y": 139.76101684570312,
        "page": 133,
        "old": 39,
        "id": "A39-026",
        "source_ids": [
          "A39-026"
        ],
        "anchor": "A39-026",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Совместимость трёх реализаций",
        "y": 235.53021240234375,
        "page": 133,
        "old": 39,
        "id": "A39-027",
        "source_ids": [
          "A39-027"
        ],
        "anchor": "A39-027",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Общий Rust-кодек через UniFFI уменьшает риск расхождения приложения и ядра, но не заменяет независимый тест. MCU может использовать другую среду, ограничения памяти и транспортный стек. Kotlin/Swift glue должен сохранять бинарные данные, cancellation и срок жизни объектов без двойного освобождения и незавершённых подписок. [55]",
        "y": 262.5478515625,
        "page": 133,
        "old": 39,
        "id": "A39-028",
        "source_ids": [
          "A39-028"
        ],
        "anchor": "A39-028",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для каждого релиза собирается матрица: старое приложение — новый Core; новый Core — старая прошивка; повторное сопряжение после обновления; откат; неизвестная capability. Контракт обещает конкретный диапазон совместимости, а не абстрактное «всё со всем».",
        "y": 350.9267883300781,
        "page": 133,
        "old": 39,
        "id": "A39-029",
        "source_ids": [
          "A39-029"
        ],
        "anchor": "A39-029",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Расширенная логическая запись наблюдения включает интервал измерения, неопределённость времени, происхождение, размещение и калибровку. Краткий JSON в этой главе показывает транспортный эскиз, а не все поля такого паспорта. Оценка и прогноз имеют отдельные типы и не записываются в канал настоящего измерения.",
        "id": "I27-005",
        "old": "I27",
        "edited": true,
        "source_ids": [
          "I27-005"
        ],
        "anchor": "I27-005",
        "content_status": "active"
      },
      {
        "id": "R22-001",
        "kind": "h2",
        "text": "Одинаковое намерение — одни и те же байты",
        "source_ids": [
          "R22-001"
        ],
        "anchor": "R22-001",
        "content_status": "active"
      },
      {
        "id": "R22-002",
        "kind": "p",
        "text": "Команда неизменяема целиком, включая not_after_device_us. В повторах меняются только кадр и session_seq. execution_seq монотонно растёт на канал внутри (controller key, controller_fence, boot_id); Core допускает не больше одной новой команды в ожидании на канал. Пропуск числа допустим, повторное применение старого числа — нет. После восстановления Core сначала читает high-water исполнителя.",
        "source_ids": [
          "R22-002"
        ],
        "anchor": "R22-002",
        "content_status": "active"
      },
      {
        "id": "R22-003",
        "kind": "table",
        "rows": [
          [
            "Шаг MCU",
            "Правило"
          ],
          [
            "1. Действующая привязка",
            "Проверить principal, установку, канал, boot и controller fence; история также требует действующих прав"
          ],
          [
            "2. Известный command_id в этой области",
            "Сравнить неизменяемое тело: совпало — вернуть историю без GPIO; иначе ID_CONFLICT"
          ],
          [
            "3. Неизвестный ID со старым execution_seq",
            "Номер не выше highwater не исполняется; вернуть RESULT_EVICTED"
          ],
          [
            "4. Новая команда",
            "Проверить тип операции, cause, ручной режим, ожидаемые ревизии, срок и локальные ограничения"
          ],
          [
            "5. Сериализованное применение",
            "Один владелец канала проверяет условия и меняет выход без гонки с кнопкой"
          ],
          [
            "6. Результат",
            "Запомнить известный APPLIED/REJECTED и highwater; текущий Snapshot хранить отдельно"
          ]
        ],
        "source_ids": [
          "R22-003"
        ],
        "anchor": "R22-003",
        "content_status": "active"
      },
      {
        "id": "R22-004",
        "kind": "p",
        "text": "Проверки шага 5 и изменение выхода выполняет один владелец канала. Между «проверил ручной режим» и «включил GPIO» не вклинивается событие кнопки. Если кнопка уже принята, команда должна увидеть новую manual_revision. Если команда успела первой, следующая кнопка законно изменит выход; поздний ACK не отменит её.",
        "source_ids": [
          "R22-004"
        ],
        "anchor": "R22-004",
        "content_status": "active"
      },
      {
        "id": "R22-005",
        "kind": "p",
        "text": "Кэш E0 хранит 64 последних завершённых результата на канал в RAM. Живое исполнение не вытесняется. После вытеснения high-water остаётся до конца boot, поэтому исчезновение записи не превращает старое действие в новое. При переполнении ресурса новый запрос получает RESOURCE_LIMIT до действия. После reset новая boot-привязка запрещает все прежние команды; сохранность эффекта между GPIO и записью результата не обещается. Core сохраняет неизменяемую связь исходного request/intent ID с execution_seq; гарантия ID_CONFLICT по известному ID не означает бесконечного MCU-кэша.",
        "source_ids": [
          "R22-005"
        ],
        "anchor": "R22-005",
        "content_status": "active"
      },
      {
        "id": "R22-006",
        "kind": "p",
        "text": "История: A включила свет, её ответ потерян; человек нажал OFF; пришёл повтор A. Узел находит A и возвращает прежний APPLIED с прежней ревизией. Текущее состояние остаётся OFF. Если A уже вытеснена, повтор получает RESULT_EVICTED, также без действия. Это сильнее простого совета «используйте set вместо toggle».",
        "source_ids": [
          "R22-006"
        ],
        "anchor": "R22-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "id": "R22-016",
        "text": "Снять ручной режим — отдельная операция RELEASE_MANUAL (код 2, argument=null), а не скрытое следствие следующего автоматического SET. Она разрешена только для явного действия авторизованного пользователя, проходит те же проверки boot, fence, версий и срока и увеличивает manual_revision. Сам уровень выхода не меняется. Обычный SET имеет код 1 и булев аргумент; локальная кнопка обрабатывается вне сетевого протокола. Позиция длинного нажатия «вернуть автоматику» должна отличаться от сброса и сопряжения.",
        "source_ids": [
          "R22-016"
        ],
        "anchor": "R22-016",
        "content_status": "active"
      },
      {
        "id": "R22-007",
        "kind": "h2",
        "text": "Время доставки нельзя начинать заново при получении",
        "source_ids": [
          "R22-007"
        ],
        "anchor": "R22-007",
        "content_status": "active"
      },
      {
        "id": "R22-008",
        "kind": "p",
        "text": "Обозначим C — BOOTTIME станции, D — часы MCU в текущей загрузке, θ = D − C. Станция отправляет пробу в c1; MCU принимает в d2 и отвечает в d3; ответ приходит в c4. При неотрицательных задержках и ограниченном дрейфе смещение на время пробы лежит между d3 − c4 и d2 − c1. Половина RTT не используется как доказанная задержка в одну сторону. Границы расширяются на ошибки отметок и доказанный дрейф с момента пробы. [165]",
        "source_ids": [
          "R22-008"
        ],
        "anchor": "R22-008",
        "content_status": "active"
      },
      {
        "id": "R22-009",
        "kind": "code",
        "text": "θ_min = d3 − c4 − ε\nθ_max = d2 − c1 + ε\nnot_after_device = core_deadline + θ_min − drift_margin",
        "source_ids": [
          "R22-009"
        ],
        "anchor": "R22-009",
        "content_status": "active"
      },
      {
        "id": "R22-010",
        "kind": "p",
        "text": "Пример в миллисекундах: c1=1000, d2=5008, d3=5009, c4=1020. До дополнительных ошибок θ лежит в [3989; 4008]. Для core_deadline=3000 и общего запаса 20 мс срок MCU выбирается 6969. Пакет, впервые дошедший после этого числа, не включает GPIO. Арифметика выполняется в широком знаковом типе; отрицательный срок, переполнение и недостаточный бюджет означают отказ, а не оборачивание uint64.",
        "source_ids": [
          "R22-010"
        ],
        "anchor": "R22-010",
        "content_status": "active"
      },
      {
        "id": "R22-011",
        "kind": "p",
        "text": "В первом опыте ширина допустимого интервала часов — не более 50 мс, возраст пробы — не более 30 с; это цели квалификации. Бюджет ошибки включает дрейф MCU, ошибку timestamp и допустимую частотную коррекцию часов станции. CLOCK_BOOTTIME учитывает suspend, но не является RAW-шкалой без коррекции частоты. При неизвестной границе срочная команда не допускается. Actuator E0 не спит; suspend/resume, смена источника часов и boot инвалидируют привязку. Непосредственно до GPIO проверяется now_device < not_after_device. [145]",
        "source_ids": [
          "R22-011"
        ],
        "anchor": "R22-011",
        "content_status": "active"
      },
      {
        "id": "R22-012",
        "kind": "h2",
        "text": "Четыре вида сроков",
        "source_ids": [
          "R22-012"
        ],
        "anchor": "R22-012",
        "content_status": "active"
      },
      {
        "id": "R22-013",
        "kind": "table",
        "rows": [
          [
            "Срок",
            "Шкала и восстановление"
          ],
          [
            "Новая команда / ActionProposal",
            "BOOTTIME Core → консервативный срок MCU; reboot не продлевает"
          ],
          [
            "ManualOverride E0",
            "До явного освобождения; при reboot BOOT_INHIBIT"
          ],
          [
            "Календарное расписание",
            "UTC + качество; при неизвестной дате не «догонять» опасные события"
          ],
          [
            "Приглашение / обновление",
            "Проверенный срок и отзыв; неизвестное UTC не разрешает обход проверки"
          ]
        ],
        "source_ids": [
          "R22-013"
        ],
        "anchor": "R22-013",
        "content_status": "active"
      },
      {
        "id": "R22-014",
        "kind": "p",
        "text": "UNKNOWN описывает недостаток знания об отправленном действии. StatusQuery может уточнить его позже; это не команда повторить действие. CANCELLED гарантируется только до передачи исполнителю. После DISPATCHED запрос отмены не обещает отменить уже состоявшийся физический эффект. При переключении телефона с Wi-Fi на мобильную сеть приложение сначала спрашивает статус прежнего ID.",
        "source_ids": [
          "R22-014"
        ],
        "anchor": "R22-014",
        "content_status": "active"
      },
      {
        "id": "R22-015",
        "kind": "figure",
        "figure": "R01",
        "caption": "Пример интервальной привязки часов. Дедлайн создаётся один раз в шкале MCU и не продлевается временем доставки.",
        "source_ids": [
          "R22-015"
        ],
        "anchor": "R22-015",
        "content_status": "active"
      }
    ],
    "origins": [
      "A39"
    ],
    "question": "Как связать удобный пример с настоящим кодеком?",
    "intro": "Здесь мы опустимся до конкретных полей и байтов. Короткий JSON помогает увидеть идею; двоичный пример проверяет кодек; полная запись наблюдения нужна уже платформе. Это три разных представления, которые должны быть связаны явным преобразованием.\n\nНе будем делать вид, что все поля окончательно заморожены. Фиксируем небольшой учебный профиль и тестовые примеры, а расширения вводим с версией и отрицательными тестами. Так ранняя сборка не запирает будущую систему в случайном формате.",
    "id": "ch-22",
    "slug": "chapter-22"
  },
  {
    "number": 23,
    "title": "Первое подключение и смена владельца",
    "group": "Доверие",
    "units": [
      {
        "kind": "lead",
        "text": "Передать пароль Wi-Fi и назначить владельца — две разные операции. Удобство не должно стирать эту границу.",
        "y": 137.3555450439453,
        "page": 73,
        "old": 21,
        "id": "A21-001",
        "source_ids": [
          "A21-001"
        ],
        "anchor": "A21-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Мобильное приложение снимает важное ограничение браузера: оно может использовать платформенные BLE-интерфейсы и хранить закреплённую идентичность станции. Поэтому Koobique не требуется публичный сертификат только ради обычного локального управления. Но первая передача доверия всё равно должна быть физически подтверждена и защищена.",
        "y": 188.46142578125,
        "page": 73,
        "old": 21,
        "id": "A21-002",
        "source_ids": [
          "A21-002"
        ],
        "anchor": "A21-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Лабораторный E0: сначала надёжный базовый путь",
        "y": 284.2305908203125,
        "page": 73,
        "old": 21,
        "id": "A21-003",
        "source_ids": [
          "A21-003"
        ],
        "anchor": "A21-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первую станцию на имеющемся ПК или Mini-ITX допустимо настроить через локальную консоль либо доверенное USB-подключение. Она получает Wi-Fi-профиль, создаёт собственный ключ и показывает QR с идентификатором, отпечатком и одноразовым билетом владельца. Телефон считывает его непосредственно с доверенного экрана или распечатки, полученной при локальной настройке.",
        "y": 311.24822998046875,
        "page": 73,
        "old": 21,
        "id": "A21-004",
        "source_ids": [
          "A21-004"
        ],
        "anchor": "A21-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Это честный путь первого самостоятельно собранного экземпляра. Он не выдаётся за окончательный пользовательский onboarding и не требует постоянного Ethernet. Основное приложение после настройки работает в обычной Wi-Fi-сети.",
        "y": 399.627197265625,
        "page": 73,
        "old": 21,
        "id": "A21-005",
        "source_ids": [
          "A21-005"
        ],
        "anchor": "A21-005",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Устройства: BLE + готовый защищённый provisioning",
        "y": 463.44635009765625,
        "page": 73,
        "old": 21,
        "id": "A21-006",
        "source_ids": [
          "A21-006"
        ],
        "anchor": "A21-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для ESP32 можно использовать ESP-IDF Unified Provisioning с Security 2. Документация описывает SRP6a и AES-GCM, уникальные данные владения и готовые Android/iOS-инструменты. BLE сохраняет соединение с телефоном во время присоединения к Wi-Fi. Security 0 и режим без доказательства владения не выбираются для обычной настройки Koobique. [70]",
        "y": 490.4639892578125,
        "page": 73,
        "old": 21,
        "id": "A21-007",
        "source_ids": [
          "A21-007"
        ],
        "anchor": "A21-007",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A28",
        "caption": "Два этапа: сначала защищённая настройка сети, затем проверяемое назначение станции и рабочих полномочий.",
        "y": 56.69291687011719,
        "page": 74,
        "old": 21,
        "id": "A21-008",
        "source_ids": [
          "A21-008"
        ],
        "anchor": "A21-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Приложение передаёт SSID, пароль, ожидаемую идентичность станции и короткоживущий билет присоединения. Узел подключается к сети, устанавливает защищённый канал со станцией и подтверждает билет. Только после этого он считается принадлежащим инсталляции. Успешный DHCP сам по себе не означает успешное сопряжение.",
        "y": 469.4766540527344,
        "page": 74,
        "old": 21,
        "id": "A21-009",
        "source_ids": [
          "A21-009"
        ],
        "anchor": "A21-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Рабочие сертификаты или ключи отличаются от секрета первого владения. QR на корпусе не становится бессрочным административным паролем. Повторное открытие режима требует физического действия и видимого индикатора.",
        "y": 541.8805541992188,
        "page": 74,
        "old": 21,
        "id": "A21-010",
        "source_ids": [
          "A21-010"
        ],
        "anchor": "A21-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Как сделать такую же настройку Linux-станции",
        "y": 605.6997680664062,
        "page": 74,
        "old": 21,
        "id": "A21-011",
        "source_ids": [
          "A21-011"
        ],
        "anchor": "A21-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "На Linux нет автоматического эквивалента ESP-IDF provisioning только от установки BlueZ. Есть три пути. Первый — локальный USB/консоль для E0. Второй — отдельный ESP-сопроцессор настройки на внутреннем USB, использующий уже испытанный provisioning и передающий структурированные данные привилегированному helper. Третий — собственный Linux BLE-сервис с проверенным стандартным протоколом установления защищённой сессии.",
        "y": 632.7174072265625,
        "page": 74,
        "old": 21,
        "id": "A21-012",
        "source_ids": [
          "A21-012"
        ],
        "anchor": "A21-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сопроцессор сокращает часть программного риска, но добавляет доверенный компонент и свою прошивку. Его нельзя считать простым «проводком»: он видит сетевые секреты и должен быть связан с идентичностью хоста. Собственный BLE-сервис уменьшает детали, но требует отдельного аудита handshake, replay-защиты и мобильной совместимости. GATT API BlueZ — транспортный инструмент, а не готовая модель безопасности. [42]",
        "y": 737.0713500976562,
        "page": 74,
        "old": 21,
        "id": "A21-013",
        "also_preserves": [
          "A21-014"
        ],
        "source_ids": [
          "A21-013"
        ],
        "anchor": "A21-013",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Путь к удобному E1",
        "y": 130.12698364257812,
        "page": 75,
        "old": 21,
        "id": "A21-015",
        "source_ids": [
          "A21-015"
        ],
        "anchor": "A21-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для E0 остаётся локальный доверенный bootstrap. Для E1 выбирается один BLE-путь после отдельного стенда Android/iOS. Временный SoftAP допустим как явно обозначенный резерв настройки, но не как обязательная сеть для повседневного управления.",
        "y": 148.58815002441406,
        "page": 75,
        "old": 21,
        "id": "A21-016",
        "source_ids": [
          "A21-016"
        ],
        "anchor": "A21-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Атомарность сопряжения",
        "y": 220.8658905029297,
        "page": 75,
        "old": 21,
        "id": "A21-017",
        "source_ids": [
          "A21-017"
        ],
        "anchor": "A21-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Процесс имеет состояния DISCOVERED, AUTHENTICATED, NETWORK_READY, CLAIM_PENDING и OWNED. До OWNED команда управления запрещена. Билет имеет срок, nonce и ограниченное число попыток. Если питание исчезло после выдачи ключа, стороны при возвращении сверяют состояние, а не создают две записи устройства.",
        "y": 247.883544921875,
        "page": 75,
        "old": 21,
        "id": "A21-018",
        "source_ids": [
          "A21-018"
        ],
        "anchor": "A21-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Если два телефона пытаются присвоить один узел, применяется правило первого подтверждённого владельца в физически открытом окне. Изменение владельца — отдельная процедура сброса/переноса с очисткой старых полномочий. Простое удаление плитки из приложения не гарантирует отзыв ключа на устройстве.",
        "y": 320.2874755859375,
        "page": 75,
        "old": 21,
        "id": "A21-019",
        "source_ids": [
          "A21-019"
        ],
        "anchor": "A21-019",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Несколько пользователей",
        "y": 400.0816345214844,
        "page": 75,
        "old": 21,
        "id": "A21-020",
        "source_ids": [
          "A21-020"
        ],
        "anchor": "A21-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Владелец приглашает другого пользователя подписанным ограниченным приглашением: комнаты, возможности, цель, срок, nonce и привязка к получателю. Использование и отзыв фиксируются устойчиво; повтор того же завершения возвращает прежний результат, а другой ключ не присваивает приглашение. Это отдельный Client API договор, который ещё требует реализации и отрицательных тестов. Разрешение включать свет не предоставляет SSH, обновление прошивок или чтение личных данных.",
        "y": 427.0992736816406,
        "page": 75,
        "old": 21,
        "id": "A21-021",
        "source_ids": [
          "A21-021"
        ],
        "anchor": "A21-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Потерянный пользовательский session credential отзывается на Core: закрываются сессии и подписки, отменяются ещё не отправленные запросы. Если телефон также владеет owner-authority key для переноса Hub, требуется отдельный отзыв этого полномочия на каждом MCU. Обычный отзыв клиента не завершает обновление списка доверенных owner-подписантов. Узел, ещё не подтвердивший новый список, остаётся PENDING; прямое управление устройством, если появится, также требует собственного договора прав.",
        "y": 499.5032043457031,
        "page": 75,
        "old": 21,
        "id": "A21-022",
        "source_ids": [
          "A21-022"
        ],
        "anchor": "A21-022",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Смена роутера и восстановление",
        "y": 579.2974243164062,
        "page": 75,
        "old": 21,
        "id": "A21-023",
        "source_ids": [
          "A21-023"
        ],
        "anchor": "A21-023",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Пользователь должен иметь способ передать новый Wi-Fi-профиль локально без перепривязки всей комнаты. Это операция сетевой конфигурации, не смена владельца. Физическая кнопка станции открывает ограниченный режим восстановления; индикатор и приложение показывают, что сейчас разрешено.",
        "y": 606.3150024414062,
        "page": 75,
        "old": 21,
        "id": "A21-024",
        "source_ids": [
          "A21-024"
        ],
        "anchor": "A21-024",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Нельзя делать «factory reset» единственным ответом на ошибочный пароль: он уничтожает контекст и превращает обслуживаемую систему в набор одноразовых устройств. Полный сброс требует длительного подтверждения и ясно различается с обычным перезапуском.",
        "y": 678.7189331054688,
        "page": 75,
        "old": 21,
        "id": "A21-025",
        "source_ids": [
          "A21-025"
        ],
        "anchor": "A21-025",
        "content_status": "active"
      },
      {
        "id": "R23-001",
        "kind": "h2",
        "text": "Ключ создаётся там, где будет использоваться",
        "source_ids": [
          "R23-001"
        ],
        "anchor": "R23-001",
        "content_status": "active"
      },
      {
        "id": "R23-002",
        "kind": "p",
        "text": "Рабочая приватная пара узла генерируется на MCU при проверенной энтропии; наружу передаётся публичный ключ или запрос на удостоверение. Станция не рассылает всем платам заранее созданные секретные ключи. Provisioning использует уникальный случайный PoP/verifier; примерные общие значения SDK не копируются в изделия. Для E0 предлагаем не менее 128 бит случайного секрета подключения с QR-представлением.",
        "source_ids": [
          "R23-002"
        ],
        "anchor": "R23-002",
        "content_status": "active"
      },
      {
        "id": "R23-003",
        "kind": "p",
        "text": "Билет присоединения связывает installation_id, purpose, node_id, публичный ключ узла, challenge, права и срок/условия завершения. Его нельзя использовать для другой установки или переслать другому ключу как универсальную ссылку. Формат подписанного объекта опирается на COSE_Sign1; собственный формат шифрования не разрабатывается. [149]",
        "source_ids": [
          "R23-003"
        ],
        "anchor": "R23-003",
        "content_status": "active"
      },
      {
        "id": "R23-004",
        "kind": "h2",
        "text": "Сопряжение с незавершённым финалом",
        "source_ids": [
          "R23-004"
        ],
        "anchor": "R23-004",
        "content_status": "active"
      },
      {
        "id": "R23-005",
        "kind": "p",
        "text": "После физического окна и проверки доказательств узел сохраняет CLAIM_PENDING с хешем билета и transaction_id. Повтор того же завершения возвращает тот же результат; другой билет не подменяет pending-состояние. OWNED показывается после устойчивой записи и подтверждения обеих сторон. Если ответ потерян, приложение сверяет транзакцию. Оно не предлагает factory reset как первый способ исправить любой тайм-аут.",
        "source_ids": [
          "R23-005"
        ],
        "anchor": "R23-005",
        "content_status": "active"
      },
      {
        "id": "R23-006",
        "kind": "p",
        "text": "Factory reset требует отдельного физического действия, стирает рабочую привязку и сетевые секреты согласно профилю, но не превращает запись старого Hub в подтверждение нового владельца. У старой станции остаётся «узел сброшен/недоступен» до подтверждённого удаления. Смена пароля Wi-Fi открывает узкое окно повторной настройки сети без автоматического снятия владельца и ручного режима.",
        "source_ids": [
          "R23-006"
        ],
        "anchor": "R23-006",
        "content_status": "active"
      }
    ],
    "origins": [
      "A21"
    ],
    "question": "Как станция узнаёт своего владельца, а устройство — свою станцию?",
    "intro": "Доступность по IP доказывает только то, что кто-то отвечает по этому адресу. Доверие требует другого основания: физически подтверждённого подключения, индивидуальных ключей и проверки ожидаемой стороны.\n\nПервый самостоятельно собранный экземпляр допускает сервисный шаг через USB или локальную настройку. Но обычное использование после него остаётся мобильным и беспроводным. Смена пароля Wi-Fi, смена владельца и восстановление ключа — разные операции, их нельзя объединять в одну кнопку «сброс».",
    "id": "ch-23",
    "slug": "chapter-23"
  },
  {
    "number": 24,
    "title": "Кому система разрешает действовать",
    "group": "Безопасность",
    "units": [
      {
        "kind": "lead",
        "text": "Защита строится как согласованная цепочка от физической кнопки до обновления. TLS защищает один из её участков; полномочия и переходы доверия проверяются во всём контуре.",
        "y": 168.675537109375,
        "page": 98,
        "old": 29,
        "id": "A29-001",
        "source_ids": [
          "A29-001"
        ],
        "anchor": "A29-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В Koobique есть несколько активов: полномочия владельца, идентичность станции, ключи устройств, история присутствия, голосовые данные, возможность включить нагрузку и канал обновления. Угрозы тоже различаются: сосед в Wi-Fi, украденный телефон, вредоносный пакет, подменённый relay, старый backup и физический доступ к отладочному разъёму.",
        "y": 219.5513153076172,
        "page": 98,
        "old": 29,
        "id": "A29-002",
        "source_ids": [
          "A29-002"
        ],
        "anchor": "A29-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Модель доверия",
        "y": 308.0309753417969,
        "page": 98,
        "old": 29,
        "id": "A29-003",
        "source_ids": [
          "A29-003"
        ],
        "anchor": "A29-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Постоянная идентичность устройства не равна рабочему разрешению управлять им. Владелец назначает станцию контроллером; станция выдаёт ограниченные полномочия пользователям и сервисам. Фабричная подлинность, если появится в будущих экземплярах, не должна позволять производителю незаметно заменить владельца.",
        "y": 334.8184509277344,
        "page": 98,
        "old": 29,
        "id": "A29-004",
        "source_ids": [
          "A29-004"
        ],
        "anchor": "A29-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первый личный комплект может создавать ключи при доверенной локальной настройке. Важно не копировать один приватный ключ во все прошивки. Уникальная идентичность и процедуру отзыва нужно сделать сразу — исправлять это после развёртывания гораздо сложнее.",
        "y": 402.6239929199219,
        "page": 98,
        "old": 29,
        "id": "A29-005",
        "source_ids": [
          "A29-005"
        ],
        "anchor": "A29-005",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "TLS в локальном приложении",
        "y": 476.0656433105469,
        "page": 98,
        "old": 29,
        "id": "A29-006",
        "source_ids": [
          "A29-006"
        ],
        "anchor": "A29-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Приложение получает доверие к станции через QR/физически подтверждённую настройку. Затем проверяет закреплённый ключ или локальную цепочку доверия, ожидаемую идентичность и параметры сессии. Локальное имя, например стабильное имя станции в .local, может использоваться как ожидаемый SAN/SNI независимо от способа обнаружения IP.",
        "y": 502.8531799316406,
        "page": 98,
        "old": 29,
        "id": "A29-007",
        "source_ids": [
          "A29-007"
        ],
        "anchor": "A29-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Глобальный trustAll запрещён. Для публичной PKI не отключаются имя, цепочка и календарная валидность. Собственный локальный pin-профиль ниже является отдельной моделью доверия, проверяющей конкретный ключ и подпись handshake; он не реализуется общим флагом «игнорировать ошибки сертификата». Ротация и отзыв закреплённых ключей входят в жизненный цикл установки. [167][148]",
        "y": 585.69677734375,
        "page": 98,
        "old": 29,
        "id": "A29-008",
        "source_ids": [
          "A29-008"
        ],
        "anchor": "A29-008",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Что делать с неверным временем",
        "y": 659.1383666992188,
        "page": 98,
        "old": 29,
        "id": "A29-009",
        "source_ids": [
          "A29-009"
        ],
        "anchor": "A29-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Календарная дата и измерение длительности решаются раздельно. У ESP32-C6 есть RTC-таймер и высокоточный таймер, но не готовое доверенное календарное время после полного отключения. В офлайн-профиле первая аутентификация опирается на заранее проверенную идентичность ключа; после неё станция передаёт оценку UTC и качества. Часы не создают доверие к незнакомой станции. [146]",
        "y": 685.9259033203125,
        "page": 98,
        "old": 29,
        "id": "A29-010",
        "source_ids": [
          "A29-010"
        ],
        "anchor": "A29-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Решение — отдельный ограниченный режим восстановления, открываемый физическим действием и подтверждаемый уже закреплённой идентичностью либо новым доверенным bootstrap. В нём можно восстановить время и обновить локальные сертификаты. Это не обычный управляющий API с отключённой проверкой. Бессрочную автономность нельзя строить на незаметном принятии любого истёкшего сертификата.",
        "y": 57.198036193847656,
        "page": 99,
        "old": 29,
        "id": "A29-011",
        "source_ids": [
          "A29-011"
        ],
        "anchor": "A29-011",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Минимальные полномочия",
        "y": 145.67767333984375,
        "page": 99,
        "old": 29,
        "id": "A29-012",
        "source_ids": [
          "A29-012"
        ],
        "anchor": "A29-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Датчик публикует только свои каналы и не может выполнять команды на другом устройстве. Speech-worker может предложить намерение, но не менять пользователей. Приложение гостя управляет разрешённой комнатой, но не экспортирует ключи. Privileged-helper принимает ограниченные операции от конкретного локального клиента.",
        "y": 172.4652099609375,
        "page": 99,
        "old": 29,
        "id": "A29-013",
        "source_ids": [
          "A29-013"
        ],
        "anchor": "A29-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сетевые лимиты нужны до аутентификации: число handshake, размер сообщения, длительность операции, скорость ошибок. Иначе корректный TLS не защитит от исчерпания памяти. Парсер любого внешнего файла обновления, QR и диагностического импорта также считается недоверенной границей.",
        "y": 240.270751953125,
        "page": 99,
        "old": 29,
        "id": "A29-014",
        "source_ids": [
          "A29-014"
        ],
        "anchor": "A29-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Загрузка и обновление",
        "y": 313.7123718261719,
        "page": 99,
        "old": 29,
        "id": "A29-015",
        "source_ids": [
          "A29-015"
        ],
        "anchor": "A29-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Secure Boot и проверка образа уменьшают риск незаметной замены системного ПО. dm-verity может проверять целостность блочного образа, но доверие к корневому хешу должно происходить из проверенной загрузочной цепочки. Шифрование диска решает другую задачу — доступ к данным при извлечении накопителя. [92]",
        "y": 340.4999084472656,
        "page": 99,
        "old": 29,
        "id": "A29-016",
        "source_ids": [
          "A29-016"
        ],
        "anchor": "A29-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для личного проекта полезен режим собственных ключей владельца. Переход в developer mode должен быть физически подтверждён и иметь понятные последствия для секретов. Нельзя одновременно обещать свободную замену любой прошивки и защиту секретов от такой прошивки без отдельной аппаратной модели.",
        "y": 408.3054504394531,
        "page": 99,
        "old": 29,
        "id": "A29-017",
        "source_ids": [
          "A29-017"
        ],
        "anchor": "A29-017",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Старые копии и два контроллера",
        "y": 481.7471008300781,
        "page": 99,
        "old": 29,
        "id": "A29-018",
        "source_ids": [
          "A29-018"
        ],
        "anchor": "A29-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "При восстановлении станции нельзя просто запустить копию базы рядом с исходной. Устройство должно знать актуальный controller_fence или иной fencing-токен. После официального переноса старый контроллер отвергается. Если такой механизм ещё не реализован, процедура восстановления обязана сначала изолировать старую станцию.",
        "y": 508.5346374511719,
        "page": 99,
        "old": 29,
        "id": "A29-019",
        "source_ids": [
          "A29-019"
        ],
        "anchor": "A29-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сохранённый backup может содержать уже отозванные полномочия. Поэтому восстановление включает сверку и ротацию, а не слепое доверие всему архиву. Ключ восстановления хранится отдельно и не отправляется в техподдержку.",
        "y": 576.3401489257812,
        "page": 99,
        "old": 29,
        "id": "A29-020",
        "source_ids": [
          "A29-020"
        ],
        "anchor": "A29-020",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Проверки безопасности до постоянной эксплуатации",
        "y": 646.4581298828125,
        "page": 99,
        "old": 29,
        "id": "A29-021",
        "source_ids": [
          "A29-021"
        ],
        "anchor": "A29-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Подменить endpoint, использовать чужой ключ, повторить старый билет, отправить слишком большой кадр, восстановить старую конфигурацию и проверить отзыв телефона. Отдельно проверить, что диагностический архив не содержит приватных ключей и Wi-Fi-паролей.",
        "y": 664.9192504882812,
        "page": 99,
        "old": 29,
        "id": "A29-022",
        "source_ids": [
          "A29-022"
        ],
        "anchor": "A29-022",
        "content_status": "active"
      },
      {
        "kind": "lead",
        "text": "Файл весов — программный артефакт. Показания датчика — недоверенный вход. История присутствия — личная информация даже без камеры.",
        "id": "I20-001",
        "old": "I20",
        "source_ids": [
          "I20-001"
        ],
        "anchor": "I20-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Подлинность не равна правильности",
        "id": "I20-002",
        "old": "I20",
        "source_ids": [
          "I20-002"
        ],
        "anchor": "I20-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Аутентифицированный узел может быть неправильно установлен, повреждён или захвачен. Криптография подтверждает источник и целостность в пределах модели угроз, но не истинность физического показания. Проверяем диапазон, изменение, статус канала и согласованность с другими источниками. Подозрительная запись остаётся доступной для диагностики, не получает молча доверенный статус.",
        "id": "I20-003",
        "old": "I20",
        "source_ids": [
          "I20-003"
        ],
        "anchor": "I20-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Строки имён, голосовой текст и загружаемые описания не становятся инструкциями привилегированному коду. Планировщик видит зарегистрированные ресурсы с машинными ID; человекочитаемое имя не определяет путь файла или сетевой адрес исполнения.",
        "id": "I20-004",
        "old": "I20",
        "source_ids": [
          "I20-004"
        ],
        "anchor": "I20-004",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Модель как поставляемый пакет",
        "id": "I20-005",
        "old": "I20",
        "source_ids": [
          "I20-005"
        ],
        "anchor": "I20-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Манифест содержит хеш, версию, архитектуру, входные и выходные типы, нормализацию, ограничения памяти, backend, происхождение, лицензию и результаты допуска. Обновляется весь комплект, а не только веса. Прежний токенизатор или порядок признаков способен изменить смысл корректного нового файла.",
        "id": "I20-006",
        "old": "I20",
        "source_ids": [
          "I20-006"
        ],
        "anchor": "I20-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для безопасной доставки полезны роли, метаданные и проверки обновлений TUF. [84] Они помогают защищать цепочку поставки, но не доказывают качество модели. Safetensors предназначен для безопасной сериализации тензоров без произвольного pickle-кода; это не защита от всех ошибок загрузчика, огромных размеров и небезопасных native-операторов. [138]",
        "id": "I20-007",
        "old": "I20",
        "source_ids": [
          "I20-007"
        ],
        "anchor": "I20-007",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I20",
        "caption": "Две независимые цепочки: доверие к доставленному артефакту и допуск его поведения в конкретной установке. Подпись автора не является правом включить устройство владельца.",
        "id": "I20-008",
        "old": "I20",
        "source_ids": [
          "I20-008"
        ],
        "anchor": "I20-008",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Сandbox для расширений",
        "id": "I20-009",
        "old": "I20",
        "source_ids": [
          "I20-009"
        ],
        "anchor": "I20-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Если появятся пользовательские алгоритмы, можно исследовать WebAssembly с явным набором импортируемых возможностей и лимитами. Wasmtime описывает изоляцию памяти и модель доступа как часть своей безопасности. [133] Но произвольный host-call снова расширяет доверенную базу: модуль, которому разрешили командовать реле напрямую, обходит архитектуру независимо от формата байткода.",
        "id": "I20-010",
        "old": "I20",
        "source_ids": [
          "I20-010"
        ],
        "anchor": "I20-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первой версии не нужен публичный магазин плагинов. Встроенные алгоритмы и ограниченный AI-worker проще проверить. Чужой модуль допускаем только к чтению объявленных данных и выдаче предложений.",
        "id": "I20-011",
        "old": "I20",
        "source_ids": [
          "I20-011"
        ],
        "anchor": "I20-011",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Приватность производных данных",
        "id": "I20-012",
        "old": "I20",
        "source_ids": [
          "I20-012"
        ],
        "anchor": "I20-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Из редких событий света и присутствия можно получить распорядок; embedding голосового эпизода также может содержать чувствительную информацию. Поэтому «мы не храним исходный звук» не является полной политикой приватности.",
        "id": "I20-013",
        "old": "I20",
        "source_ids": [
          "I20-013"
        ],
        "anchor": "I20-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Права задаются отдельно на наблюдения, производные оценки, экспорт, обучение и удалённое чтение. Удаление данных должно учитывать производные файлы, резервные копии и включение в датасеты. Если данные уже повлияли на обученную модель, простое удаление строки не доказывает удаление этого влияния; нужна политика переобучения или отказа от такого обещания.",
        "id": "I20-014",
        "old": "I20",
        "source_ids": [
          "I20-014"
        ],
        "anchor": "I20-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Шифрование диска помогает при определённых угрозах, но не скрывает открытые данные от администратора работающего хоста. Для домашней установки это нужно честно объяснить владельцам личных носимых устройств.",
        "id": "I20-015",
        "old": "I20",
        "source_ids": [
          "I20-015"
        ],
        "anchor": "I20-015",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Сеть и администрирование",
        "id": "I20-016",
        "old": "I20",
        "source_ids": [
          "I20-016"
        ],
        "anchor": "I20-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Локальное сопряжение, пользовательский удалённый канал и SSH имеют разные полномочия. Для первого экземпляра отдельный VPN-контур и защищённая прикладная сессия остаются прозрачным вариантом. Тяжёлое обучение может работать на существующем ПК, но остановка этого вычислительного помощника не останавливает локальную платформу.",
        "id": "I20-017",
        "old": "I20",
        "edited": true,
        "source_ids": [
          "I20-017"
        ],
        "anchor": "I20-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "На станции нет обязательного облачного AI-вызова. Для внешнего исследования Jev или другого API данные готовятся отдельно и передаются только с разрешения. Удалённый доступ не обещается при полном отсутствии доступного канала связи.",
        "id": "I20-018",
        "old": "I20",
        "source_ids": [
          "I20-018"
        ],
        "anchor": "I20-018",
        "content_status": "active"
      },
      {
        "kind": "quote",
        "text": "Модель получает данные и выдаёт предложение. Секреты устройств, административные команды и подпись разрешения остаются вне её контекста и процесса.",
        "id": "I20-019",
        "old": "I20",
        "source_ids": [
          "I20-019"
        ],
        "anchor": "I20-019",
        "content_status": "active"
      },
      {
        "id": "R24-001",
        "kind": "h2",
        "text": "Два профиля TLS: публичный и локально закреплённый",
        "source_ids": [
          "R24-001"
        ],
        "anchor": "R24-001",
        "content_status": "active"
      },
      {
        "id": "R24-002",
        "kind": "p",
        "text": "Обычное публичное HTTPS продолжает проверять цепочку, имя, срок сертификата и подпись handshake. Для первого офлайн-соединения собственных узлов E0 выбирает другой, явно выделенный договор: закреплённый публичный ключ конкретной стороны, проверенный через физический bootstrap. X.509 здесь служит контейнером ключа; календарный срок не является источником доверия. Нельзя переносить этот verifier на произвольные сайты или неизвестных peers. [167][148]",
        "source_ids": [
          "R24-002"
        ],
        "anchor": "R24-002",
        "content_status": "active"
      },
      {
        "id": "R24-003",
        "kind": "p",
        "text": "Локальная проверка обязана сравнить полный разрешённый SPKI/его криптографический отпечаток, доказательство владения ключом в TLS 1.3, назначение ключа и текущую привязку установки. Проверка подписи handshake не отключается. Механизм raw public key стандартизован RFC 7250, но его готовая поддержка на обеих наших сторонах ещё не установлена. Для E0 нужен отрицательный опыт с чужим ключом, подменённым контейнером и неверной подписью; до него pin-профиль остаётся проектным, а не «готовой безопасной библиотекой». [147]",
        "source_ids": [
          "R24-003"
        ],
        "anchor": "R24-003",
        "content_status": "active"
      },
      {
        "id": "R24-004",
        "kind": "p",
        "text": "После аутентификации Core передаёт оценку календарного времени с качеством; это не делает её точной автоматически. Внешний NTS — дополнительный источник, не условие работы лампы. При неизвестном UTC обычное TUF-обновление и календарные приглашения не обходят срок. Аварийный recovery — отдельная физически подтверждаемая процедура с ограниченной целью, не глобальный переключатель trustAll. [166][84] Core долговечно хранит последнее подтверждённое доверенное время как нижнюю границу, вместе с происхождением и качеством оценки. Откат системных часов или старого backup не должен незаметно уменьшать эту границу. Она не доказывает точное текущее UTC и не заменяет оценку неопределённости; формат передачи времени и политика восстановления требуют отдельного контракта и отрицательных испытаний.",
        "source_ids": [
          "R24-004"
        ],
        "anchor": "R24-004",
        "content_status": "active"
      },
      {
        "id": "R24-005",
        "kind": "h2",
        "text": "Владение отдельно от работающей станции",
        "source_ids": [
          "R24-005"
        ],
        "anchor": "R24-005",
        "content_status": "active"
      },
      {
        "id": "R24-006",
        "kind": "p",
        "text": "Полномочие владельца для редких подписей переноса отделяется от рабочего session credential телефона. Предусмотрен независимый offline recovery key; перед постоянной эксплуатацией проверяется восстановление без исходного телефона. Эти ключи не являются копиями неэкспортируемого Secure Enclave. Аппаратная защита Keystore/StrongBox зависит от конкретного устройства и настройки. Набор доверенных owner/recovery keys, его поколение и правила замены — отдельный контракт; биометрия и голос сами по себе не передают роль владельца. [177][71]",
        "source_ids": [
          "R24-006"
        ],
        "anchor": "R24-006",
        "content_status": "active"
      },
      {
        "id": "R24-007",
        "kind": "p",
        "text": "Перенос на новый Hub подписывает действующее owner-authority полномочие. Назначение связывает purpose, installation_id, node_id, свежий challenge, previous_fence, next_fence=previous+1 и новый controller key. MCU проверяет подписанта по текущему устойчивому owner/recovery-набору, сохраняет целое назначение атомарно и затем отвечает. Обновление этого набора отдельно определяет авторизацию, защиту от rollback и случай последнего утраченного ключа; controller_fence не отзывает owner-подписанта. Формат OwnerSetUpdate, транспорт TrustTransfer и отрицательные TLS/COSE-проверки остаются открытым gate, а не реализованной функцией.",
        "source_ids": [
          "R24-007"
        ],
        "anchor": "R24-007",
        "content_status": "active"
      },
      {
        "id": "R24-008",
        "kind": "p",
        "text": "Перенос завершён по каждому устройству отдельно. Offline-узел остаётся PENDING; нельзя подписать «весь дом перенесён», если часть устройств ещё доверяет старой станции. Для этих узлов сначала изолируют старый Hub и выполняют подтверждённое переназначение при возвращении связи. Отказ питания в середине записи должен оставить либо целую старую, либо целую новую привязку, но не пустой режим без владельца.",
        "source_ids": [
          "R24-008"
        ],
        "anchor": "R24-008",
        "content_status": "active"
      },
      {
        "id": "R24-009",
        "kind": "p",
        "text": "Обычный backup содержит данные и публичные назначения, но не копию активного controller private key. На новом Hub ключ создаётся заново; восстановление начинается пассивно. Если платформа реально поддерживает TPM/sealing, это отдельный проверяемый способ защиты ключа. E0 без защиты физического доступа не обещает сопротивление клонированию всей flash/диска злоумышленником.",
        "source_ids": [
          "R24-009"
        ],
        "anchor": "R24-009",
        "content_status": "active"
      },
      {
        "id": "R24-010",
        "kind": "h2",
        "text": "Отзыв телефона — больше, чем удалить запись",
        "source_ids": [
          "R24-010"
        ],
        "anchor": "R24-010",
        "content_status": "active"
      },
      {
        "id": "R24-011",
        "kind": "p",
        "text": "После commit отзыва рабочего клиента Core закрывает сессии и подписки, отменяет неотправленные действия и приглашения и повторяет ACL-проверку перед dispatch. Для owner-authority отзыва необходимо обновить доверие каждого MCU и получить подтверждение; до этого отзыв соответствующего узла не завершён. Уже отправленное действие нельзя гарантированно отменить по оборванной сети: действует исходный срок. TLS resumption, если будет разрешён, обязан учитывать текущее поколение авторизации; его отключение в E0 допустимо для уменьшения объёма квалификации.",
        "source_ids": [
          "R24-011"
        ],
        "anchor": "R24-011",
        "content_status": "active"
      },
      {
        "id": "R24-012",
        "kind": "p",
        "text": "После восстановления старого backup пользовательские сессии и разрешения не оживают автоматически: выдаётся новое поколение авторизации, требуется подтверждение владельца и повторное подключение телефонов. Старая подписанная ссылка также не становится новой. Изменение политики доверия должно переживать OTA-rollback; иначе возврат старой прошивки одновременно возвратит отозванный ключ.",
        "source_ids": [
          "R24-012"
        ],
        "anchor": "R24-012",
        "content_status": "active"
      },
      {
        "id": "R24-013",
        "kind": "table",
        "rows": [
          [
            "Субъект",
            "Разрешённый ресурс",
            "Операции"
          ],
          [
            "Владелец",
            "Своя установка",
            "Управление, приглашения, перенос, восстановление"
          ],
          [
            "Пользователь комнаты",
            "Назначенные каналы",
            "Просмотр, ручное действие; без ключей и OTA"
          ],
          [
            "Микрофон комнаты",
            "Белый список световых сценариев",
            "Ограниченное предложение; без SSH и выдачи доступа"
          ],
          [
            "Support",
            "Временный диагностический профиль",
            "Только явно разрешённые чтения/операции"
          ],
          [
            "AI-worker",
            "Предоставленный снимок",
            "Предложение; никаких сетевых или физических полномочий"
          ]
        ],
        "source_ids": [
          "R24-013"
        ],
        "anchor": "R24-013",
        "content_status": "active"
      },
      {
        "id": "R24-014",
        "kind": "figure",
        "figure": "R02",
        "caption": "Перенос владения: новый ключ, полномочие владельца и подтверждение каждого узла. Большего числа от старого рабочего ключа недостаточно.",
        "source_ids": [
          "R24-014"
        ],
        "anchor": "R24-014",
        "content_status": "active"
      }
    ],
    "origins": [
      "A29",
      "I20"
    ],
    "question": "Как ограничить ущерб от ошибки программы или модели?",
    "intro": "Безопасность здесь состоит из нескольких независимых вопросов. Кто подключился? Что ему разрешено? Насколько свежи данные? Можно ли физически выполнить действие? Останутся ли секреты закрытыми при падении отдельной службы?\n\nTLS отвечает только на часть этой цепочки. Поэтому полномочия датчика, приложения, речевой модели и SSH-администратора различаются. Модель может ошибиться в выборе цели, но её процесс не должен иметь ключ, которым можно напрямую включить любой выход.",
    "id": "ch-24",
    "slug": "chapter-24"
  },
  {
    "number": 25,
    "title": "Управление из другой сети",
    "group": "Связь",
    "units": [
      {
        "kind": "lead",
        "text": "Внешняя сеть переносит защищённый поток. Решение о доступе остаётся на станции.",
        "y": 168.675537109375,
        "page": 76,
        "old": 22,
        "id": "A22-001",
        "source_ids": [
          "A22-001"
        ],
        "anchor": "A22-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "За пределами дома у телефона нет прямого локального маршрута к хабу. Дом может находиться за CGNAT, а входящие соединения — быть запрещены. Поэтому первая реализуемая схема строится на исходящих соединениях и небольшом сервере-посреднике под контролем владельца. Этот сервер не хранит модель комнаты и не исполняет автоматизации.",
        "y": 219.78143310546875,
        "page": 76,
        "old": 22,
        "id": "A22-002",
        "source_ids": [
          "A22-002"
        ],
        "anchor": "A22-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Три архитектуры удалённого пути",
        "y": 315.55059814453125,
        "page": 76,
        "old": 22,
        "id": "A22-003",
        "source_ids": [
          "A22-003"
        ],
        "anchor": "A22-003",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Вариант",
            "Механика",
            "Плюс",
            "Минус"
          ],
          [
            "WireGuard через VPS",
            "Телефон и хаб подключаются к VPN-узлу",
            "Понятный проверенный транспорт",
            "Настройка VPN и зависимость от UDP"
          ],
          [
            "Opaque relay на 443",
            "Хаб держит исходящий канал, relay передаёт поток",
            "Удобнее встроить в приложение",
            "Собственная маршрутизация, лимиты, сложнее реализация"
          ],
          [
            "Прямое соединение с fallback",
            "Сначала поиск прямого пути, затем relay",
            "Меньше задержка при удаче",
            "NAT traversal и смена сети усложняют систему"
          ]
        ],
        "y": 344.67,
        "page": 76,
        "old": 22,
        "id": "A22-004",
        "source_ids": [
          "A22-004"
        ],
        "anchor": "A22-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "WireGuard использует стандартную для него модель ключей и защищённого туннеля. Persistent keepalive может помочь сохранению NAT-состояния, но не гарантирует прохождение блокировок. Его частота выбирается с учётом мобильной батареи и сетевой среды. [59][72]",
        "y": 545.438232421875,
        "page": 76,
        "old": 22,
        "id": "A22-005",
        "source_ids": [
          "A22-005"
        ],
        "anchor": "A22-005",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A29",
        "caption": "Первый удалённый профиль. VPN-сервер маршрутизирует трафик; приложение дополнительно проверяет идентичность станции и прикладные права.",
        "y": 56.69291687011719,
        "page": 77,
        "old": 22,
        "id": "A22-006",
        "source_ids": [
          "A22-006"
        ],
        "anchor": "A22-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Первый работающий удалённый экземпляр",
        "y": 444.5834045410156,
        "page": 77,
        "old": 22,
        "id": "A22-007",
        "source_ids": [
          "A22-007"
        ],
        "anchor": "A22-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для E0 предлагается штатный WireGuard-клиент на телефоне и станции, VPS владельца и маршрут только к хабу/нужным адресам. Это не окончательный бесшовный UX, но работающая проверяемая основа. Внутри туннеля сохраняется защищённая прикладная сессия Koobique, чтобы VPN-сервер не становился единственным основанием доверия.",
        "y": 471.60107421875,
        "page": 77,
        "old": 22,
        "id": "A22-008",
        "source_ids": [
          "A22-008"
        ],
        "anchor": "A22-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Приложение выбирает локальный endpoint дома и VPN-endpoint вне дома, проверяя один и тот же ключ станции. Переход не создаёт новую команду при повторе: сохраняется прежний command_id. Локальный доступ не отправляется через удалённый VPS только потому, что тот доступен.",
        "y": 559.9800415039062,
        "page": 77,
        "old": 22,
        "id": "A22-009",
        "source_ids": [
          "A22-009"
        ],
        "anchor": "A22-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Интеграция VPN внутрь KMP-приложения — отдельная платформенная задача с разрешениями и жизненным циклом ОС. На iOS её нельзя считать обычным сокетом без дополнительных возможностей приложения. Для E1 сравнивается встроенный туннель и relay без системного VPN; результат требует реального мобильного стенда.",
        "y": 632.3839721679688,
        "page": 77,
        "old": 22,
        "id": "A22-010",
        "source_ids": [
          "A22-010"
        ],
        "anchor": "A22-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Что должен делать будущий relay",
        "y": 56.314456939697266,
        "page": 78,
        "old": 22,
        "id": "A22-011",
        "source_ids": [
          "A22-011"
        ],
        "anchor": "A22-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Relay принимает аутентифицированную регистрацию станции, ограничивает число каналов и объём данных, не доверяет произвольным идентификаторам маршрута и не хранит расшифрованные команды. Конечная сессия между приложением и станцией защищается независимо. Компрометация relay всё равно раскрывает часть метаданных: время соединения, адреса, объём и доступность.",
        "y": 83.33208465576172,
        "page": 78,
        "old": 22,
        "id": "A22-012",
        "source_ids": [
          "A22-012"
        ],
        "anchor": "A22-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Relay на TCP/443 не является «неблокируемым протоколом». Белые списки, блокировка IP, обрыв связи и политика сети могут остановить удалённый путь. Это ограничение честно отражается в приложении; локальные функции не зависят от попыток восстановления внешнего канала.",
        "y": 171.71102905273438,
        "page": 78,
        "old": 22,
        "id": "A22-013",
        "source_ids": [
          "A22-013"
        ],
        "anchor": "A22-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "SSH как отдельный административный контур",
        "y": 251.50521850585938,
        "page": 78,
        "old": 22,
        "id": "A22-014",
        "source_ids": [
          "A22-014"
        ],
        "anchor": "A22-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "SSH включён для владельца, но не публикуется в интернет автоматически. Вход по ключам, отдельная учётная запись, запрет общего пароля и прямого root-входа. OpenSSH позволяет настроить эти ограничения и дополнительные параметры сеанса. [60]",
        "y": 278.5228576660156,
        "page": 78,
        "old": 22,
        "id": "A22-015",
        "source_ids": [
          "A22-015"
        ],
        "anchor": "A22-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Право управлять светом не равно праву открыть shell. Техподдержка получает временный доступ по конкретному ключу, с явным разрешением владельца. Не нужен общий сервисный ключ всей группы проектов или скрытый постоянный reverse tunnel.",
        "y": 350.9267883300781,
        "page": 78,
        "old": 22,
        "id": "A22-016",
        "source_ids": [
          "A22-016"
        ],
        "anchor": "A22-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Команда обновления по SSH вызывает штатную проверяемую процедуру, а не произвольную замену бинарника в работающей системе. Диагностический пакет маскирует ключи, Wi-Fi-пароли и персональные данные. Временный доступ можно отозвать независимо от мобильных пользователей.",
        "y": 407.355712890625,
        "page": 78,
        "old": 22,
        "id": "A22-017",
        "source_ids": [
          "A22-017"
        ],
        "anchor": "A22-017",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Восстановление без внешней инфраструктуры",
        "y": 487.14990234375,
        "page": 78,
        "old": 22,
        "id": "A22-018",
        "source_ids": [
          "A22-018"
        ],
        "anchor": "A22-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "При недоступном VPS сохраняются локальный Wi-Fi, доверенный сервисный путь и физическое восстановление. Инструкция не должна требовать зайти на наш сайт, чтобы вернуть собственный дом. Конфигурация удалённого сервера экспортируется, а протокол не привязывается навсегда к одному домену производителя.",
        "y": 514.1675415039062,
        "page": 78,
        "old": 22,
        "id": "A22-019",
        "source_ids": [
          "A22-019"
        ],
        "anchor": "A22-019",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Приёмка удалённого пути",
        "y": 602.8413696289062,
        "page": 78,
        "old": 22,
        "id": "A22-020",
        "source_ids": [
          "A22-020"
        ],
        "anchor": "A22-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Проверить телефон в мобильной сети, смену Wi-Fi на LTE/5G, временную потерю VPS, смену адреса дома и повтор команды во время переключения. Убедиться, что VPS не может выдать себя за станцию и что обычный пользователь не получает SSH.",
        "y": 621.3025512695312,
        "page": 78,
        "old": 22,
        "id": "A22-021",
        "source_ids": [
          "A22-021"
        ],
        "anchor": "A22-021",
        "content_status": "active"
      },
      {
        "id": "R25-001",
        "kind": "h2",
        "text": "Туннель не отменяет авторизацию приложения",
        "source_ids": [
          "R25-001"
        ],
        "anchor": "R25-001",
        "content_status": "active"
      },
      {
        "id": "R25-002",
        "kind": "p",
        "text": "WireGuard-пир на VPS не получает право обращаться ко всем службам Hub. На wg-интерфейсе firewall разрешает только нужный порт приложения; SSH — отдельное явное разрешение и собственные ключи. Между приложением и Hub сохраняется проверка конечной идентичности и пользовательских полномочий. Компрометация ретранслятора не должна превращаться в административную локальную сеть.",
        "source_ids": [
          "R25-002"
        ],
        "anchor": "R25-002",
        "content_status": "active"
      },
      {
        "id": "R25-003",
        "kind": "p",
        "text": "Headscale/DERP — вариант уменьшить собственный код транспортной инфраструктуры; он сравнивается с простым WireGuard для личного E0 и встроенным relay для E1. Ни один вариант не гарантирует удалённый доступ при недоступности всех разрешённых внешних каналов. Локальная работа остаётся независимой. В мобильном опыте проверяются sleep, смена Wi-Fi/LTE, отзыв и незавершённая команда. [175]",
        "source_ids": [
          "R25-003"
        ],
        "anchor": "R25-003",
        "content_status": "active"
      }
    ],
    "origins": [
      "A22"
    ],
    "question": "Как управлять домом снаружи, не делая облако его хозяином?",
    "intro": "Дома приложение обращается к станции напрямую. В другой сети нужен дополнительный путь: VPN либо проверенный ретранслятор. При этом конечная сторона, которой доверяет приложение, остаётся той же станцией.\n\nДля первой сборки важнее прозрачная схема с понятными ключами и восстановлением, чем обещание незаметно пройти любую сеть. Блокировка удалённого пути не должна разрушать локальную работу, а доступ пользователя не должен автоматически давать SSH-права.",
    "id": "ch-25",
    "slug": "chapter-25"
  },
  {
    "number": 26,
    "title": "Как физический мир попадает в данные",
    "group": "Измерения",
    "units": [
      {
        "kind": "lead",
        "text": "Перед выбором AI определяется измеряемая величина каждого канала и информация, которую можно получить из наблюдений. Пропущенные физические сведения нельзя считать восстановленными лишь потому, что данные обрабатывает нейросеть.",
        "id": "I02-001",
        "old": "I02",
        "source_ids": [
          "I02-001"
        ],
        "anchor": "I02-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Измеряемая величина и вопрос пользователя",
        "id": "I02-002",
        "old": "I02",
        "source_ids": [
          "I02-002"
        ],
        "anchor": "I02-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Пользователь спрашивает: «Мне достаточно света?» Датчик выдаёт освещённость в своей точке и ориентации. Между этими формулировками стоят расположение рабочего места, тень от монитора, направленность светильника и выбранный человеком уровень комфорта. В первой версии цель должна звучать уже: «поддерживать согласованный диапазон освещённости на выбранном участке стола».",
        "id": "I02-003",
        "old": "I02",
        "source_ids": [
          "I02-003"
        ],
        "anchor": "I02-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Метрология различает измеряемую величину, результат измерения и неопределённость. Для Koobique это означает: число без описания условий недостаточно для решения. В качестве базового языка используем понятия VIM и GUM, не превращая каждый домашний датчик в лабораторный прибор. [100]",
        "id": "I02-004",
        "old": "I02",
        "source_ids": [
          "I02-004"
        ],
        "anchor": "I02-004",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Источник",
            "Полезное свидетельство",
            "Не делать такой вывод"
          ],
          [
            "Освещённость",
            "Свет в указанной точке и ориентации",
            "«Везде в комнате светло»"
          ],
          [
            "Температура",
            "Температура измерительного узла в его условиях",
            "«Человек не перегревается»"
          ],
          [
            "PIR",
            "Событие движения в зоне обнаружения",
            "«Нет движения — нет человека»"
          ],
          [
            "Радар",
            "Признаки движения/присутствия в своей конфигурации",
            "«Известно число и личность людей»"
          ],
          [
            "Ток нагрузки",
            "Электрическое потребление",
            "«Светильник даёт нужный свет»"
          ],
          [
            "BLE телефона",
            "Доступность радиоустройства",
            "«Владелец точно находится рядом»"
          ],
          [
            "Микрофон",
            "Акустический сигнал",
            "«Голос принадлежит авторизованному владельцу»"
          ]
        ],
        "id": "I02-005",
        "old": "I02",
        "source_ids": [
          "I02-005"
        ],
        "anchor": "I02-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Таблица задаёт консервативные границы нашего контракта, а не полную спецификацию любого датчика соответствующего класса. Отдельная модель оборудования может предоставлять дополнительные измерения, но их нужно подтвердить документацией и испытанием.",
        "id": "I02-006",
        "old": "I02",
        "source_ids": [
          "I02-006"
        ],
        "anchor": "I02-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Наблюдаемость: можно ли вообще различить ситуации",
        "id": "I02-007",
        "old": "I02",
        "source_ids": [
          "I02-007"
        ],
        "anchor": "I02-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Представим две комнаты с одинаковыми тремя показаниями: 23 °C, 180 лк и отсутствие события PIR. В одной комнате никого нет, в другой человек неподвижно читает. При таком наборе данных однозначный вывод о присутствии невозможен. Более сложный классификатор может использовать статистическое предположение, но не устранит эту неоднозначность.",
        "id": "I02-008",
        "old": "I02",
        "source_ids": [
          "I02-008"
        ],
        "anchor": "I02-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Нужен другой источник информации: например, признаки радара, событие двери, ручное подтверждение или более длинная история. Даже после этого остаются условия, при которых состояния неразличимы. Поэтому «неизвестно» — нормальное состояние комнаты, а не ошибка парсинга.",
        "id": "I02-009",
        "old": "I02",
        "source_ids": [
          "I02-009"
        ],
        "anchor": "I02-009",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I02",
        "caption": "Размещаем датчики по измерительной задаче. Станция — источник тепла, движения вентиляторов и звука; ставить все сенсоры внутрь куба удобно, но физически неверно для первого кабинета. Схема не в масштабе.",
        "id": "I02-010",
        "old": "I02",
        "source_ids": [
          "I02-010"
        ],
        "anchor": "I02-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Пространство — часть данных",
        "id": "I02-011",
        "old": "I02",
        "source_ids": [
          "I02-011"
        ],
        "anchor": "I02-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для светового датчика фиксируем плоскость и ориентацию; для температуры — место и способ вентиляции корпуса; для радара — направление, конфигурацию зоны и возможные движущиеся помехи. Перестановка датчика создаёт новую ревизию размещения. Она может обесценить калибровку влияния лампы, но не обязана менять криптографическую идентичность платы.",
        "id": "I02-012",
        "old": "I02",
        "source_ids": [
          "I02-012"
        ],
        "anchor": "I02-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В модели мира храним физический объект отдельно от электронного устройства. Один прибор измеряет несколько объектов; один объект наблюдается несколькими приборами. Реле и светильник — не одно и то же. После замены лампы устройство реле остаётся прежним, а модель нагрузки требует повторной проверки.",
        "id": "I02-013",
        "old": "I02",
        "source_ids": [
          "I02-013"
        ],
        "anchor": "I02-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Качество не означает максимальное число датчиков",
        "id": "I02-014",
        "old": "I02",
        "source_ids": [
          "I02-014"
        ],
        "anchor": "I02-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Дополнительный сенсор оправдан, когда он разрешает важную неоднозначность. Второй одинаково установленный датчик иногда повторяет ту же систематическую ошибку. Независимый способ проверки может быть полезнее более дорогого варианта той же микросхемы.",
        "id": "I02-015",
        "old": "I02",
        "source_ids": [
          "I02-015"
        ],
        "anchor": "I02-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первого кабинета используем Sense и Presence, разобранные в главах 11–16. Температуру и свет измеряем вне корпуса станции. Сначала наблюдаем их поведение при включении лампы, работе вентиляторов, закрытии шторы и неподвижном сидении. Только затем решаем, нужен ли ещё один канал.",
        "id": "I02-016",
        "old": "I02",
        "edited": true,
        "source_ids": [
          "I02-016"
        ],
        "anchor": "I02-016",
        "content_status": "active"
      },
      {
        "kind": "lead",
        "text": "Сбор информации начинается с механики датчика, его питания и момента измерения. Хранилище получает результат уже сформированного измерительного тракта.",
        "id": "I03-001",
        "old": "I03",
        "source_ids": [
          "I03-001"
        ],
        "anchor": "I03-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Три способа построить сеть",
        "id": "I03-002",
        "old": "I03",
        "source_ids": [
          "I03-002"
        ],
        "anchor": "I03-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Удалённые микроконтроллерные узлы. Предпочтительный путь кабинета: ESP32-C6 рядом с датчиками, локальные I²C/UART-соединения, Wi-Fi до станции. Станция получает уже типизированные измерения и диагностические флаги. Размещение измерителя не зависит от места куба; короткие периферийные провода легче исследовать. Электрические соединения показаны в монтажных картах главы 16.",
        "id": "I03-003",
        "old": "I03",
        "edited": true,
        "source_ids": [
          "I03-003"
        ],
        "anchor": "I03-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Прямое подключение к Linux. USB-измеритель или плата с поддерживаемым драйвером подключается к станции. Это удобно для микрофона и лабораторного стенда. Для температурных и других преобразователей Linux предоставляет подсистему Industrial I/O: каналы, буферы и триггеры. Поддержка конкретного устройства проверяется отдельно. [105] Перенос медленного опроса в приложение не заменяет аппаратный момент выборки.",
        "id": "I03-004",
        "old": "I03",
        "source_ids": [
          "I03-004"
        ],
        "anchor": "I03-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Проводные распределённые узлы. В мастерской можно добавить RS-485 или CAN через подходящие трансиверы. Это не новый смысловой протокол: KQIP может получить отдельный транспортный профиль. Электрические длины, терминация, изоляция и защита потребуют собственных схем. Не проводим I²C по всему помещению только потому, что на коротком столе он заработал.",
        "id": "I03-005",
        "old": "I03",
        "source_ids": [
          "I03-005"
        ],
        "anchor": "I03-005",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I03",
        "caption": "Тракт наблюдения. Ошибки возможны на каждом этапе; корректный TLS-пакет не исправляет нагретый корпус сенсора или переполненный буфер выборок.",
        "id": "I03-006",
        "old": "I03",
        "source_ids": [
          "I03-006"
        ],
        "anchor": "I03-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Техническое задание на канал",
        "id": "I03-007",
        "old": "I03",
        "source_ids": [
          "I03-007"
        ],
        "anchor": "I03-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для каждого канала задаём физическую величину, рабочий диапазон, допустимую неопределённость для сценария, период измерения, задержку, способ синхронизации и поведение при отказе. Отдельно определяем, нужны ли исходные отсчёты или достаточно агрегата.",
        "id": "I03-008",
        "old": "I03",
        "source_ids": [
          "I03-008"
        ],
        "anchor": "I03-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Пример для освещения: запуск преобразования, ожидание указанного времени интеграции, чтение результата, проверка переполнения диапазона, привязка к интервалу измерения и публикация. «Получено в 12:00:01» не означает «свет измерен мгновенно в 12:00:01». Изменение времени интеграции меняет свойства канала и должно сопровождать данные.",
        "id": "I03-009",
        "old": "I03",
        "source_ids": [
          "I03-009"
        ],
        "anchor": "I03-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для температуры комнаты низкая частота часто является разумным начальным предположением, но один режим нельзя заранее назначать всем корпусам. Сначала измеряем реакцию на перемещение и влияние собственного нагрева. Выбранные SHT45 и VEML7700 подробно разобраны в главах 12–13; параметры проверяем по фактически приобретённым модулям. [15][106]",
        "id": "I03-010",
        "old": "I03",
        "edited": true,
        "source_ids": [
          "I03-010"
        ],
        "anchor": "I03-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Аналоговый вход: где появляется настоящая инженерная работа",
        "id": "I03-011",
        "old": "I03",
        "source_ids": [
          "I03-011"
        ],
        "anchor": "I03-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Если новый датчик выдаёт напряжение, тракт включает входную защиту, согласование диапазона, фильтр, аналого-цифровой преобразователь и калибровку. Номинальная разрядность ADC не равна числу точных битов полезного измерения. Для ESP32-C6 официальная документация отдельно описывает одиночный и непрерывный режимы, ослабление входа и калибровку. [141]",
        "id": "I03-012",
        "old": "I03",
        "source_ids": [
          "I03-012"
        ],
        "anchor": "I03-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Не строим измеритель сетевого напряжения прямым делителем на макетной плате. Для первого проекта используем безопасные низковольтные источники и готовые изолированные измерительные решения там, где они необходимы. Опорное напряжение, входное сопротивление и допустимое напряжение определяются даташитом конкретной ревизии, а не общим правилом «ADC принимает 3,3 В».",
        "id": "I03-013",
        "old": "I03",
        "source_ids": [
          "I03-013"
        ],
        "anchor": "I03-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Буфер, который сообщает о собственных потерях",
        "id": "I03-014",
        "old": "I03",
        "source_ids": [
          "I03-014"
        ],
        "anchor": "I03-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Прошивка собирает отсчёты в ограниченный кольцевой буфер. Прерывание фиксирует момент события или готовность блока, но не сериализует большой JSON и не пишет синхронно во flash. После переполнения отправляется число потерянных записей и диапазон последовательности. Незаметно выбросить данные хуже, чем честно сообщить о пробеле.",
        "id": "I03-015",
        "old": "I03",
        "source_ids": [
          "I03-015"
        ],
        "anchor": "I03-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сохраняем два класса трафика. Первый — управление, ошибки, подтверждения и редкие события. Второй — потоковые измерения с допустимой политикой сокращения. Поток не может вытеснить команду остановки или диагностику потери питания. При переподключении выгрузка истории уступает актуальному управлению.",
        "id": "I03-016",
        "old": "I03",
        "source_ids": [
          "I03-016"
        ],
        "anchor": "I03-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Питание и включение как режим измерения",
        "id": "I03-017",
        "old": "I03",
        "source_ids": [
          "I03-017"
        ],
        "anchor": "I03-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Пуск вентилятора, передача радио и срабатывание катушки могут совпасть с выборкой. Система не обязана знать причину каждой помехи, но должна позволять её найти: журналируем режим питания, сброс, ошибки шины и момент переключения выхода. На стенде сравниваем данные до и после разведения питания, проводов и земли. Разделение источников без общей схемы возвратных токов само по себе не является исправлением.",
        "id": "I03-018",
        "old": "I03",
        "source_ids": [
          "I03-018"
        ],
        "anchor": "I03-018",
        "content_status": "active"
      },
      {
        "kind": "quote",
        "text": "Контракт канала описывает весь измерительный тракт. Сначала определяем качество и время данных, затем способ доставки. Никакая модель не должна принимать потерянную выборку за физический ноль.",
        "id": "I03-019",
        "old": "I03",
        "source_ids": [
          "I03-019"
        ],
        "anchor": "I03-019",
        "content_status": "active"
      },
      {
        "id": "R26-001",
        "kind": "h2",
        "text": "Достоверность источника и достоверность величины",
        "source_ids": [
          "R26-001"
        ],
        "anchor": "R26-001",
        "content_status": "active"
      },
      {
        "id": "R26-002",
        "kind": "p",
        "text": "У аутентифицированного датчика всё ещё может быть закрыт корпус, застрять АЦП или смениться размещение. Поэтому health канала, качество отсчёта и пригодность к задаче — разные признаки. Подтверждённый heartbeat не поднимает capability движения до доказательства отсутствия человека. Счётчик Gap не скрывается последней удачной записью.",
        "source_ids": [
          "R26-002"
        ],
        "anchor": "R26-002",
        "content_status": "active"
      },
      {
        "id": "R26-003",
        "kind": "p",
        "text": "Сборщик ограничивает диапазон, скорость событий, размер очереди и допустимую частоту смены режима. Расчётные поля создаются отдельной производной записью с версией алгоритма. Повторные сообщения сравниваются по (device_id, channel_id, boot_id, sample_seq); один такой ключ с другим значением означает конфликт, а не правило «последний победил».",
        "source_ids": [
          "R26-003"
        ],
        "anchor": "R26-003",
        "content_status": "active"
      }
    ],
    "origins": [
      "I02",
      "I03"
    ],
    "question": "Какие сведения теряются ещё до появления числа в базе?",
    "intro": "Между комнатой и записью лежат чувствительный элемент, его корпус, аналоговый или цифровой тракт, драйвер, буфер и сеть. На каждом шаге могут измениться время, разрешение и качество.\n\nЧтобы система могла рассуждать о физическом мире, ей нужно знать этот путь. Число 23,4 без единицы, размещения и времени не является полноценным наблюдением. Более мощная модель не восстановит автоматически отсутствующий смысл.",
    "id": "ch-26",
    "slug": "chapter-26"
  },
  {
    "number": 27,
    "title": "Наблюдение — больше, чем число",
    "group": "Данные",
    "units": [
      {
        "kind": "lead",
        "text": "Физическая платформа начинается с того, что не теряет смысл измерения при передаче, хранении и повторной обработке.",
        "y": 168.675537109375,
        "page": 79,
        "old": 23,
        "id": "A23-001",
        "source_ids": [
          "A23-001"
        ],
        "anchor": "A23-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Число 23,4 без единицы, места и времени почти бесполезно. Оно может означать температуру воздуха, температуру платы, старое значение после reboot или результат активного нагрева датчика. В Koobique наблюдение является полноценной записью с происхождением и качеством, а не парой name/value.",
        "y": 219.78143310546875,
        "page": 79,
        "old": 23,
        "id": "A23-002",
        "source_ids": [
          "A23-002"
        ],
        "anchor": "A23-002",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A30",
        "caption": "Содержание наблюдения. Качество и время — часть данных, а не необязательный текст в логе.",
        "y": 299.9540710449219,
        "page": 79,
        "old": 23,
        "id": "A23-003",
        "source_ids": [
          "A23-003"
        ],
        "anchor": "A23-003",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Минимальная схема наблюдения",
        "y": 678.4660034179688,
        "page": 79,
        "old": 23,
        "id": "A23-004",
        "source_ids": [
          "A23-004"
        ],
        "anchor": "A23-004",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Группа",
            "Поля",
            "Для чего нужны"
          ],
          [
            "Источник",
            "device_id, channel_id, boot_id, sequence",
            "Дедупликация и различение перезапусков"
          ]
        ],
        "y": 707.58,
        "page": 79,
        "old": 23,
        "id": "A23-005",
        "source_ids": [
          "A23-005"
        ],
        "anchor": "A23-005",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Группа",
            "Поля",
            "Для чего нужны"
          ],
          [
            "Величина",
            "quantity, value, unit, range",
            "Предотвращение смешения разных измерений"
          ],
          [
            "Время",
            "device_monotonic, received_at, интервал измерения",
            "Порядок, возраст и оценка задержки"
          ],
          [
            "Качество",
            "valid, stale, saturated, sensor_error, transitional",
            "Безопасный выбор входов решения"
          ],
          [
            "Метод",
            "calibration_id, hardware_rev, firmware_rev",
            "Воспроизводимость и сравнение"
          ],
          [
            "Контекст",
            "placement_id, room_id, operating_mode",
            "Интерпретация физической точки"
          ]
        ],
        "y": 56.69,
        "page": 80,
        "old": 23,
        "id": "A23-006",
        "source_ids": [
          "A23-006"
        ],
        "anchor": "A23-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Единицы лучше нормализовать в доменной модели, сохраняя исходное представление для диагностики. Нельзя смешивать °C и абсолютную температуру без явного преобразования, lux и raw ADC, проценты яркости и электрическую мощность. Проверка диапазона не должна молча обрезать невозможное значение до допустимого: иногда это признак неисправности.",
        "y": 297.8392333984375,
        "page": 80,
        "old": 23,
        "id": "A23-007",
        "source_ids": [
          "A23-007"
        ],
        "anchor": "A23-007",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Два времени и интервал",
        "y": 393.6084289550781,
        "page": 80,
        "old": 23,
        "id": "A23-008",
        "source_ids": [
          "A23-008"
        ],
        "anchor": "A23-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "На устройстве измеряются монотонный счётчик и boot_id. На станции — время получения и собственные монотонные часы. Календарное UTC полезно для истории, но не является единственной основой порядка. Если часы датчика неизвестны, это отражается как неопределённость, а не маскируется временем получения.",
        "y": 420.6260986328125,
        "page": 80,
        "old": 23,
        "id": "A23-009",
        "source_ids": [
          "A23-009"
        ],
        "anchor": "A23-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для lux время относится к интервалу интегрирования. Для температуры — к моменту получения результата после измерительной операции. Для события кнопки важен фронт, а не момент отправки пакета. Эти различия особенно заметны при синхронизации нескольких сенсоров.",
        "y": 493.030029296875,
        "page": 80,
        "old": 23,
        "id": "A23-010",
        "source_ids": [
          "A23-010"
        ],
        "anchor": "A23-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Станция может оценивать смещение часов через обмен timestamp и RTT, но односторонняя задержка остаётся неопределённой при асимметрии. В модели хранится оценка ошибки. Нельзя уверенно упорядочить два физических события, если разница между ними меньше неопределённости времени.",
        "y": 565.4339599609375,
        "page": 80,
        "old": 23,
        "id": "A23-011",
        "source_ids": [
          "A23-011"
        ],
        "anchor": "A23-011",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Свежесть зависит от процесса",
        "y": 645.2281494140625,
        "page": 80,
        "old": 23,
        "id": "A23-012",
        "source_ids": [
          "A23-012"
        ],
        "anchor": "A23-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Температуре комнаты допустим больший возраст, чем событию кнопки. Для спящего датчика отсутствие heartbeat в течение нескольких секунд может быть нормой. Поэтому TTL данных задаётся по каналу и режиму, а не одним глобальным числом.",
        "y": 672.2457275390625,
        "page": 80,
        "old": 23,
        "id": "A23-013",
        "source_ids": [
          "A23-013"
        ],
        "anchor": "A23-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Истёкший TTL меняет качество наблюдения. Значение можно продолжать показывать как последнее известное, но с возрастом. В автоматизацию оно попадает только по разрешённой политике. «Нет свежей температуры» не означает 0 °C, а «нет пакетов радара» не означает пустую комнату.",
        "y": 57.42815399169922,
        "page": 81,
        "old": 23,
        "id": "A23-014",
        "source_ids": [
          "A23-014"
        ],
        "anchor": "A23-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Обработка пропусков и выбросов",
        "y": 137.22232055664062,
        "page": 81,
        "old": 23,
        "id": "A23-015",
        "source_ids": [
          "A23-015"
        ],
        "anchor": "A23-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Не все выбросы ошибочны: резкое освещение фонариком — реальное событие. Для диагностики сохраняются raw и причина фильтрации. Обычный график может показывать сглаженный ряд, но алгоритм не должен незаметно терять физические скачки, важные для сценария.",
        "y": 164.23995971679688,
        "page": 81,
        "old": 23,
        "id": "A23-016",
        "source_ids": [
          "A23-016"
        ],
        "anchor": "A23-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Пропуск не всегда интерполируется. Для визуализации короткого пропуска температуры линейная линия допустима с отметкой; для команды реле интерполяция между OFF и ON бессмысленна. У агрегата хранятся число образцов, минимум, максимум, среднее и доля валидных данных.",
        "y": 236.64389038085938,
        "page": 81,
        "old": 23,
        "id": "A23-017",
        "source_ids": [
          "A23-017"
        ],
        "anchor": "A23-017",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Происхождение решения",
        "y": 316.4380798339844,
        "page": 81,
        "old": 23,
        "id": "A23-018",
        "source_ids": [
          "A23-018"
        ],
        "anchor": "A23-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Оценка «человек присутствует» ссылается на использованные наблюдения и версию алгоритма. Решение «включить свет» ссылается на оценку, lux, политику и состояние ManualOverride. Это создаёт цепочку объяснения: пользователь видит не просто «автоматизация сработала», а причину.",
        "y": 343.4557189941406,
        "page": 81,
        "old": 23,
        "id": "A23-019",
        "source_ids": [
          "A23-019"
        ],
        "anchor": "A23-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Пересчёт старой истории новой моделью создаёт новую производную версию, не переписывая исходные наблюдения. Иначе сравнить улучшение алгоритма невозможно. Данные можно экспортировать в открытом формате вместе с описанием единиц и качества; собственный протокол не должен превращать архив в закрытую загадку.",
        "y": 415.85968017578125,
        "page": 81,
        "old": 23,
        "id": "A23-020",
        "source_ids": [
          "A23-020"
        ],
        "anchor": "A23-020",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Приватность физического мира",
        "y": 495.6538391113281,
        "page": 81,
        "old": 23,
        "id": "A23-021",
        "source_ids": [
          "A23-021"
        ],
        "anchor": "A23-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Присутствие и голос относятся к личной жизни даже без камеры. Предлагается хранить минимально необходимое: события и признаки по умолчанию, сырое аудио — только в явно включённом диагностическом режиме. Удаление пользователя и очистка истории имеют понятные границы.",
        "y": 522.6714477539062,
        "page": 81,
        "old": 23,
        "id": "A23-022",
        "source_ids": [
          "A23-022"
        ],
        "anchor": "A23-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Администратор хоста технически имеет более широкие возможности, чем пользователь приложения. Если в будущем потребуется защита личных данных от владельца станции, понадобится отдельное шифрование и перераспределение вычислений; обычный ACL внутри Core не решает эту задачу.",
        "y": 595.0753784179688,
        "page": 81,
        "old": 23,
        "id": "A23-023",
        "source_ids": [
          "A23-023"
        ],
        "anchor": "A23-023",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Проверка качества данных",
        "y": 683.7492065429688,
        "page": 81,
        "old": 23,
        "id": "A23-024",
        "source_ids": [
          "A23-024"
        ],
        "anchor": "A23-024",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Искусственно передать старый timestamp, повтор sequence, новое boot_id, насыщенное lux и heater-флаг. Проверить, что график, автоматизация и журнал различают эти случаи и не превращают их в одинаковое «валидное число».",
        "y": 702.2103881835938,
        "page": 81,
        "old": 23,
        "id": "A23-025",
        "source_ids": [
          "A23-025"
        ],
        "anchor": "A23-025",
        "content_status": "active"
      },
      {
        "kind": "lead",
        "text": "Запись измерения сохраняет значение и то, что было известно системе в момент решения. Это позволяет проверить действие по доступным тогда основаниям.",
        "id": "I05-001",
        "old": "I05",
        "source_ids": [
          "I05-001"
        ],
        "anchor": "I05-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Минимум, который нельзя выбросить ради компактности",
        "id": "I05-002",
        "old": "I05",
        "source_ids": [
          "I05-002"
        ],
        "anchor": "I05-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Предлагаем логический объект Observation: идентичность канала, объект наблюдения, величина и единица, значение или явный статус отсутствия, номер последовательности и перезапуска, время/интервал измерения, время получения станцией, качество, ревизия размещения и калибровки. На проводе постоянные поля можно вынести в согласованный дескриптор канала.",
        "id": "I05-003",
        "old": "I05",
        "source_ids": [
          "I05-003"
        ],
        "anchor": "I05-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Разделение сенсора, наблюдаемого свойства и физического объекта имеет готовый словарь в SOSA/SSN. [101] Мы используем эти идеи для совместимого смысла, но не обязуем микроконтроллер передавать RDF или длинные URI в каждом пакете. Обозначения свойств и действий из WoT Thing Description полезны для внешнего описания устройства; они не заменяют модель его воздействия на среду. [102]",
        "id": "I05-004",
        "old": "I05",
        "source_ids": [
          "I05-004"
        ],
        "anchor": "I05-004",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Группа",
            "Обязательный смысл"
          ],
          [
            "Кто и что",
            "Устройство, канал, объект, измеряемая величина, профиль схемы"
          ],
          [
            "Значение",
            "Число/событие, единица, диапазон; пропуск не равен нулю"
          ],
          [
            "Когда",
            "Часы источника, интервал интеграции, неопределённость времени, получение станцией"
          ],
          [
            "Качество",
            "Исправность, насыщение, срок свежести, признаки восстановления"
          ],
          [
            "Происхождение",
            "Перезапуск, последовательность, калибровка, размещение, преобразование"
          ],
          [
            "Связь с действием",
            "Ссылка на известное событие/команду, но не автоматическое объявление причинности"
          ]
        ],
        "id": "I05-005",
        "old": "I05",
        "source_ids": [
          "I05-005"
        ],
        "anchor": "I05-005",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Четыре времени вместо одной даты",
        "id": "I05-006",
        "old": "I05",
        "source_ids": [
          "I05-006"
        ],
        "anchor": "I05-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Время среды — когда происходило наблюдаемое явление. Часы устройства — чем это время было отмечено. Время получения — когда станция увидела пакет. Время решения — на какой снимок знаний опирался исполнитель. Для датчика с интеграцией явление относится к интервалу, а не точке.",
        "id": "I05-007",
        "old": "I05",
        "source_ids": [
          "I05-007"
        ],
        "anchor": "I05-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Метки относятся к конкретному boot_id и режиму тактирования. Для кратких сроков Linux используется CLOCK_BOOTTIME, учитывающий suspend; произвольный Rust Instant или CLOCK_MONOTONIC не считается эквивалентом без проверки реализации. После resume/reboot краткоживущие предложения инвалидируются, если их актуальность не доказана. UTC служит истории и календарным правилам, но не оживляет старый outbox. [145]",
        "id": "I05-008",
        "old": "I05",
        "source_ids": [
          "I05-008"
        ],
        "anchor": "I05-008",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I05",
        "caption": "Пакет B измерен раньше, но пришёл позже A. Он дополняет историю. Исполнитель не воспроизводит вчерашнее решение после досылки и не переписывает основания уже выполненной команды.",
        "id": "I05-009",
        "old": "I05",
        "source_ids": [
          "I05-009"
        ],
        "anchor": "I05-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Расчётный бюджет часов: при начальной границе 10 мс и доказанной для данного режима относительной ошибке 20 ppm за 30 с добавится 0,6 мс. Это только арифметический пример. Он не назначает точность внутреннему RTC RC-генератору C6 и не переносится в light/deep sleep. Рабочий профиль измеряет дрейф, температуру и смену источника; неопределённая граница означает запрет срочного действия, а не нулевую ошибку. [146]",
        "id": "I05-010",
        "old": "I05",
        "source_ids": [
          "I05-010"
        ],
        "anchor": "I05-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Поздние записи и окончательность",
        "id": "I05-011",
        "old": "I05",
        "source_ids": [
          "I05-011"
        ],
        "anchor": "I05-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В потоке выделяем события, пригодные для актуального управления, и историческую досылку. Для оценщика задаём окно допустимого опоздания. Приход новой записи может уточнить прошлую оценку, но не должен тихо сделать вид, что исполнитель знал её раньше.",
        "id": "I05-012",
        "old": "I05",
        "source_ids": [
          "I05-012"
        ],
        "anchor": "I05-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Храним decision_snapshot_id: идентификатор снимка состояния и версий моделей. Он позволяет воспроизвести логику на доступной тогда информации. Исторический пересчёт создаёт новую оценку со ссылкой на старую, а не стирает основание первоначального действия.",
        "id": "I05-013",
        "old": "I05",
        "source_ids": [
          "I05-013"
        ],
        "anchor": "I05-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Свежесть зависит от действия",
        "id": "I05-014",
        "old": "I05",
        "source_ids": [
          "I05-014"
        ],
        "anchor": "I05-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Температура десятисекундной давности может быть достаточна для графика, но не для особого процесса с быстрым нагревом. Для управления освещением важно и время измерения, и то, менялась ли лампа после него. Поэтому паспорт процесса задаёт условия свежести относительно среды и действий, а не только универсальный TTL.",
        "id": "I05-015",
        "old": "I05",
        "source_ids": [
          "I05-015"
        ],
        "anchor": "I05-015",
        "content_status": "active"
      },
      {
        "kind": "quote",
        "text": "KQIP связывает значение с согласованным контрактом наблюдения. Метаданные версионируются. Если старое устройство не передаёт сведения, необходимые для конкретного управления, область разрешённой автоматизации сужается.",
        "id": "I05-016",
        "old": "I05",
        "source_ids": [
          "I05-016"
        ],
        "anchor": "I05-016",
        "content_status": "active"
      },
      {
        "id": "N-015",
        "kind": "h2",
        "text": "Разобранный путь одного наблюдения",
        "new": true,
        "source_ids": [
          "N-015"
        ],
        "anchor": "N-015",
        "content_status": "active"
      },
      {
        "id": "N-016",
        "kind": "p",
        "text": "Пусть Sense начинает измерение на отметке своих часов 120,000 с и заканчивает на 120,015 с. Запись получает boot_id и номер 1042. Станция принимает её позже. Она сохраняет исходный интервал и отдельно свою оценку того, как он соотносится с локальной шкалой времени. Числа в этом примере иллюстрируют договор, а не паспорт длительности любого режима SHT45.",
        "new": true,
        "source_ids": [
          "N-016"
        ],
        "anchor": "N-016",
        "content_status": "active"
      },
      {
        "id": "N-017",
        "kind": "p",
        "text": "Для графика допускается поздняя вставка в историю. Для автоматизации вычисляется возраст с учётом временной неопределённости и политики процесса. Если данных недостаточно, решение становится ограниченным или откладывается. Следующий этап — оценка состояния — ссылается на ID этой записи, поэтому причину решения можно восстановить.",
        "new": true,
        "source_ids": [
          "N-017"
        ],
        "anchor": "N-017",
        "content_status": "active"
      },
      {
        "id": "R27-001",
        "kind": "h2",
        "text": "Один словарь полей на узле и в журнале",
        "source_ids": [
          "R27-001"
        ],
        "anchor": "R27-001",
        "content_status": "active"
      },
      {
        "id": "R27-002",
        "kind": "p",
        "text": "Нормативные имена E0: channel_id, sample_seq, sample_start_us, sample_end_us, boot_id, status, flags, descriptor_revision, placement_revision. Единственный status — VALID, INVALID или WARMING; независимые flags уточняют heater, saturation, delayed и другие зарегистрированные условия. Флаг не делает INVALID пригодным. Старые имена из учебных файлов преобразуются явным адаптером версии, а не принимаются как скрытые синонимы.",
        "source_ids": [
          "R27-002"
        ],
        "anchor": "R27-002",
        "content_status": "active"
      },
      {
        "id": "R27-003",
        "kind": "p",
        "text": "Временная метка получения сервером не подставляется вместо интервала измерения. Для старого буфера после reboot неизвестное соответствие UTC остаётся неизвестным. Порядок внутри boot можно сохранить даже без даты; порядок между разными устройствами и загрузками требует свидетельств. Алгоритм привязки шкал и сроков описан в главе 22.",
        "source_ids": [
          "R27-003"
        ],
        "anchor": "R27-003",
        "content_status": "active"
      },
      {
        "id": "R27-004",
        "kind": "h2",
        "text": "Что считать свидетельством света после команды",
        "source_ids": [
          "R27-004"
        ],
        "anchor": "R27-004",
        "content_status": "active"
      },
      {
        "id": "R27-005",
        "kind": "p",
        "text": "Машинное условие пригодности: совпадают канал, placement_revision, профиль лампы и калибровка; качество допустимо; нижняя граница начала измерения позже верхней границы момента действия плюс проверенное время установления; внутри интервала не было другого воздействия, меняющего тракт. Если интервал пересекает переключение, отсчёт не подтверждает установившийся эффект.",
        "source_ids": [
          "R27-005"
        ],
        "anchor": "R27-005",
        "content_status": "active"
      },
      {
        "id": "R27-006",
        "kind": "p",
        "text": "Даже пригодный рост lux может быть вызван солнцем или другой лампой. EffectEvidence хранит альтернативные объяснения и силу свидетельства. Если датчик тока не установлен, UI пишет «контроль тока не предусмотрен», а не ждёт его вечно. Историческое VERIFIED относится к конкретному эпизоду; состояние комнаты спустя минуту оценивается заново.",
        "source_ids": [
          "R27-006"
        ],
        "anchor": "R27-006",
        "content_status": "active"
      },
      {
        "id": "R3-27-EFFECT-H",
        "kind": "h2",
        "text": "Свидетельство эффекта как проверяемая запись",
        "source_ids": [],
        "anchor": "R3-27-EFFECT-H",
        "content_status": "r3_addition"
      },
      {
        "id": "R3-27-EFFECT-P1",
        "kind": "p",
        "text": "Для эпизода исполнения сохраняются command_id, сообщённый момент действия с границей ошибки времени, требуемое время установления и используемые наблюдения. Свидетельство также указывает channel_id, ревизии размещения и калибровки, профиль нагрузки и известные вмешательства. Эти поля позволяют проверить, относится ли измерение к данному действию. Если часть сведений неизвестна, запись сохраняет этот предел вместо автоматического VERIFIED.",
        "source_ids": [],
        "anchor": "R3-27-EFFECT-P1",
        "content_status": "r3_addition"
      },
      {
        "id": "R3-27-EFFECT-P2",
        "kind": "p",
        "text": "Причинный вывод сильнее совпадения во времени. Лампа могла включиться одновременно с открытием шторы; тогда рост lux совместим с несколькими объяснениями. Для проверки предполагаемого вклада полезен разрешённый чередующийся опыт OFF/ON с контролем фоновых изменений и остановкой по ручному действию. Полученный коэффициент действует только для испытанных размещения, сенсора и светильника. Электрическую исправность нельзя окончательно вывести из одного оптического наблюдения.",
        "source_ids": [],
        "anchor": "R3-27-EFFECT-P2",
        "content_status": "r3_addition"
      }
    ],
    "origins": [
      "A23",
      "I05"
    ],
    "question": "Как система отличает свежий факт от поздно доставленной истории?",
    "intro": "Пусть датчик измерил температуру, потерял Wi-Fi и прислал запись через минуту. Время доставки новое, но сама температура относится к прошлому. Для графика запись полезна; для немедленного решения её может быть недостаточно.\n\nПоэтому у наблюдения есть собственная временная область и происхождение. Перезагрузка датчика начинает новую эпоху его часов. Пропуск, насыщение и недостоверная калибровка передаются явно, а не заменяются нулём или бесконечно повторяемым последним значением.",
    "id": "ch-27",
    "slug": "chapter-27"
  },
  {
    "number": 28,
    "title": "Частота, фильтр и калибровка",
    "group": "Измерения",
    "units": [
      {
        "kind": "lead",
        "text": "Фильтр подавляет выбранные колебания сигнала и одновременно добавляет задержку. Его параметры определяют по времени физического процесса и по тому, для какого решения используются данные.",
        "id": "I04-001",
        "old": "I04",
        "source_ids": [
          "I04-001"
        ],
        "anchor": "I04-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Частоты измерения, передачи и решения",
        "id": "I04-002",
        "old": "I04",
        "source_ids": [
          "I04-002"
        ],
        "anchor": "I04-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Частота преобразования сенсора, чтения драйвера, отправки пакетов и записи истории может различаться. Например, узел собирает короткое окно отсчётов, отправляет среднее с минимумом и максимумом, а история позднее хранит минутные агрегаты. Нужно сохранить число исходных отсчётов и длительность окна: одно среднее не сообщает, были ли внутри короткие провалы.",
        "id": "I04-003",
        "old": "I04",
        "source_ids": [
          "I04-003"
        ],
        "anchor": "I04-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Если интересующий сигнал меняется быстро, редкая выборка может показать ложную медленную картину. Это наложение частот нельзя надёжно убрать цифровым фильтром после того, как информация потеряна. Для собственного аналогового канала фильтр до ADC и частота выборки проектируются вместе. Для цифрового датчика исследуем внутреннее преобразование и интеграцию, а не только частоту вызова функции чтения.",
        "id": "I04-004",
        "old": "I04",
        "source_ids": [
          "I04-004"
        ],
        "anchor": "I04-004",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Три фильтра и их цена",
        "id": "I04-005",
        "old": "I04",
        "source_ids": [
          "I04-005"
        ],
        "anchor": "I04-005",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Метод",
            "Когда полезен",
            "Что можно потерять"
          ],
          [
            "Скользящее среднее",
            "Случайные колебания вокруг медленного сигнала",
            "Быстрое изменение растягивается по окну"
          ],
          [
            "Медиана окна",
            "Отдельные выбросы при достаточной частоте",
            "Короткое настоящее событие может исчезнуть"
          ],
          [
            "Экспоненциальное сглаживание",
            "Малое состояние и регулируемая инерция",
            "Возникает запаздывание; коэффициент зависит от шага времени"
          ]
        ],
        "id": "I04-006",
        "old": "I04",
        "source_ids": [
          "I04-006"
        ],
        "anchor": "I04-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для последнего варианта удобно задавать постоянную времени KQMATH001TOKEN, а не произвольную долю предыдущего значения:",
        "id": "I04-007",
        "old": "I04",
        "source_ids": [
          "I04-007"
        ],
        "anchor": "I04-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "KQMATH000TOKEN",
        "id": "I04-008",
        "old": "I04",
        "source_ids": [
          "I04-008"
        ],
        "anchor": "I04-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Здесь KQMATH002TOKEN — новое измерение, KQMATH003TOKEN — прошедшее время. При нерегулярных пакетах такой коэффициент лучше выражает физический смысл фильтра. Однако большой пробел не превращает новое измерение в знание всей пропущенной траектории.",
        "id": "I04-009",
        "old": "I04",
        "source_ids": [
          "I04-009"
        ],
        "anchor": "I04-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Расчётный пример: сглаживание света. При KQMATH004TOKEN с и шаге KQMATH005TOKEN с получаем KQMATH006TOKEN. После ступенчатого изменения идеальная модель фильтра достигает примерно 63% нового уровня за 2 с. Это может быть приемлемо для графика, но слишком медленно для подтверждения включения лампы. Поэтому график и проверка действия используют разные представления одного исходного сигнала.",
        "id": "I04-010",
        "old": "I04",
        "source_ids": [
          "I04-010"
        ],
        "anchor": "I04-010",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I04",
        "caption": "Три представления одного потока: исходные наблюдения, сглаженное значение для интерфейса и отдельный детектор физического изменения. Сглаженная линия не подменяет первичные данные.",
        "id": "I04-011",
        "old": "I04",
        "source_ids": [
          "I04-011"
        ],
        "anchor": "I04-011",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Признаки для моделей",
        "id": "I04-012",
        "old": "I04",
        "source_ids": [
          "I04-012"
        ],
        "anchor": "I04-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для присутствия полезны длительность отсутствия движения, число событий в окне, качество связи, согласованность источников и признак ручного взаимодействия. Необходимо знать, какие из признаков наблюдаемы в момент решения. «Через минуту человек нажал кнопку» нельзя использовать при обучении модели, которая должна решить сейчас.",
        "id": "I04-013",
        "old": "I04",
        "source_ids": [
          "I04-013"
        ],
        "anchor": "I04-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Нормализация, единицы, маски пропусков и границы окон — часть версии модели. Если тренировочный конвейер использовал секунды, а устройство передало миллисекунды, корректная нейросеть получит физически другой вход. Проверяем преобразования на постоянных тестовых примерах до сравнения качества AI.",
        "id": "I04-014",
        "old": "I04",
        "source_ids": [
          "I04-014"
        ],
        "anchor": "I04-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Калибровка и дрейф",
        "id": "I04-015",
        "old": "I04",
        "source_ids": [
          "I04-015"
        ],
        "anchor": "I04-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Калибровка связывает показания с опорой в заданных условиях; подгонка под соседний непроверенный датчик даёт согласованность, но не независимое доказательство точности. Практически начинаем с сравнения повторяемости, затем изучаем влияние корпуса и размещения. Для световой модели отдельно измеряем вклад лампы в нескольких режимах фона.",
        "id": "I04-016",
        "old": "I04",
        "source_ids": [
          "I04-016"
        ],
        "anchor": "I04-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Храним исходное значение, применённую поправку и идентификатор калибровки. Исправление коэффициента не должно незаметно переписывать историю, на основании которой вчера приняли решение. Новая интерпретация старых данных оформляется новой производной записью.",
        "id": "I04-017",
        "old": "I04",
        "source_ids": [
          "I04-017"
        ],
        "anchor": "I04-017",
        "content_status": "active"
      },
      {
        "id": "R28-001",
        "kind": "h2",
        "text": "Инициализация фильтра — тоже часть алгоритма",
        "source_ids": [
          "R28-001"
        ],
        "anchor": "R28-001",
        "content_status": "active"
      },
      {
        "id": "R28-002",
        "kind": "p",
        "text": "EMA начинается с первого допустимого значения либо с явно заданной начальной оценки. При большом пропуске применяется политика reset или расчёт коэффициента по реальному Δt; нельзя молча выполнить один обычный шаг как после регулярной выборки. После смены калибровки, gain или размещения фильтр не смешивает несовместимые шкалы.",
        "source_ids": [
          "R28-002"
        ],
        "anchor": "R28-002",
        "content_status": "active"
      },
      {
        "id": "R28-003",
        "kind": "p",
        "text": "Перед уменьшением частоты нужен фильтр против наложения частот, согласованный с наблюдаемым процессом. Но программная фильтрация не восстанавливает уже потерянные сенсором быстрые колебания. Для расчёта светового эффекта учитывается групповая задержка фильтра и интервал физического измерения; красивый сглаженный график не обязательно лучший вход быстрого решения. [106]",
        "source_ids": [
          "R28-003"
        ],
        "anchor": "R28-003",
        "content_status": "active"
      }
    ],
    "origins": [
      "I04"
    ],
    "question": "Как убрать шум, не спрятав полезное изменение?",
    "intro": "Фильтр делает линию спокойнее, но одновременно меняет задержку. Если слишком сильно усреднить свет, система поздно заметит затемнение. Если слишком часто измерять медленный процесс, поток данных вырастет без сопоставимой пользы.\n\nВыбираем частоту и фильтр по изменению, которое хотим обнаружить. Сохраняем различие между сырым отсчётом и обработанным значением. Исправление калибровки также имеет версию: иначе через месяц будет невозможно понять, почему одна и та же история выглядит иначе.",
    "id": "ch-28",
    "slug": "chapter-28"
  },
  {
    "number": 29,
    "title": "Что известно, а что только вероятно",
    "group": "Оценка состояния",
    "units": [
      {
        "kind": "lead",
        "text": "Уверенность модели, погрешность измерителя и право управлять реле — три разные сущности. Их нельзя свести к одному числу от нуля до единицы.",
        "id": "I06-001",
        "old": "I06",
        "source_ids": [
          "I06-001"
        ],
        "anchor": "I06-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Как выглядит честная оценка",
        "id": "I06-002",
        "old": "I06",
        "source_ids": [
          "I06-002"
        ],
        "anchor": "I06-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Предлагаемый объект Estimate содержит оцениваемую величину или состояние, значение/распределение, вид неопределённости, использованные наблюдения, модель, срок применимости и условия. Если вероятностная модель ещё не откалибрована, интерфейс может показывать «есть свидетельства присутствия», а не точные 97,4%.",
        "id": "I06-003",
        "old": "I06",
        "source_ids": [
          "I06-003"
        ],
        "anchor": "I06-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для измерений оцениваем неопределённость тракта. Для модели — ошибку предсказания и несоответствие текущих условий обучающим. Для состояния — неоднозначность свидетельств. Разрешение действия — отдельный результат правил доступа и физических ограничений.",
        "id": "I06-004",
        "old": "I06",
        "source_ids": [
          "I06-004"
        ],
        "anchor": "I06-004",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Пример бюджета температуры",
        "id": "I06-005",
        "old": "I06",
        "source_ids": [
          "I06-005"
        ],
        "anchor": "I06-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Предположим независимые стандартные неопределённости: калибровка 0,12 °C, повторяемость 0,03 °C, влияние корпуса 0,25 °C и размещения 0,50 °C. В упрощённой линейной модели:",
        "id": "I06-006",
        "old": "I06",
        "source_ids": [
          "I06-006"
        ],
        "anchor": "I06-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "KQMATH008TOKEN",
        "id": "I06-007",
        "old": "I06",
        "source_ids": [
          "I06-007"
        ],
        "anchor": "I06-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В реальном бюджете нужно учитывать чувствительности и корреляции; основа такого расчёта — GUM. [100] Значение KQMATH010TOKEN °C нельзя без предпосылок объявить точным 95-процентным интервалом. Вывод примера другой: дорогой сенсор внутри неправильно размещённого корпуса не исправляет доминирующий источник ошибки.",
        "id": "I06-008",
        "old": "I06",
        "source_ids": [
          "I06-008"
        ],
        "anchor": "I06-008",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I06",
        "caption": "Четыре независимых вопроса. Высокая уверенность классификатора не компенсирует просроченные данные, не выдаёт права пользователю и не подтверждает исправность нагрузки.",
        "id": "I06-009",
        "old": "I06",
        "source_ids": [
          "I06-009"
        ],
        "anchor": "I06-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Объединение присутствия",
        "id": "I06-010",
        "old": "I06",
        "source_ids": [
          "I06-010"
        ],
        "anchor": "I06-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Можно начать с автомата: присутствие подтверждено, присутствие вероятно, пусто подтверждено для данного сценария, неизвестно. Переходы зависят от событий, таймеров и здоровья каналов. Это понятный эталон для будущего вероятностного фильтра.",
        "id": "I06-011",
        "old": "I06",
        "source_ids": [
          "I06-011"
        ],
        "anchor": "I06-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В байесовском варианте обновляется вероятность скрытого состояния. Учебный пример: до сигнала вероятность присутствия равна 0,6; вероятность положительного сигнала радара при присутствии — 0,9, при отсутствии — 0,2. Тогда",
        "id": "I06-012",
        "old": "I06",
        "source_ids": [
          "I06-012"
        ],
        "anchor": "I06-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "KQMATH009TOKEN",
        "id": "I06-013",
        "old": "I06",
        "source_ids": [
          "I06-013"
        ],
        "anchor": "I06-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Эти числа заданы для иллюстрации, не взяты из паспорта радара. Их придётся оценить в кабинете. После изменения чувствительности или размещения датчика старые частоты ошибок могут перестать подходить.",
        "id": "I06-014",
        "old": "I06",
        "source_ids": [
          "I06-014"
        ],
        "anchor": "I06-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Почему десять свидетельств иногда равны одному",
        "id": "I06-015",
        "old": "I06",
        "source_ids": [
          "I06-015"
        ],
        "anchor": "I06-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сотня последовательных кадров одного радара не обязательно даёт сто независимых подтверждений. PIR и радар могут реагировать на один движущийся объект; несколько моделей могут использовать одни и те же исходные признаки. Простое перемножение вероятностей создаст чрезмерную уверенность.",
        "id": "I06-016",
        "old": "I06",
        "source_ids": [
          "I06-016"
        ],
        "anchor": "I06-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В первой реализации явно группируем зависимые источники и ограничиваем накопление уверенности по повторным кадрам. Позднее можно использовать совместную модель наблюдений. Но даже сложная модель обязана отличать неизвестную зависимость от доказанной независимости.",
        "id": "I06-017",
        "old": "I06",
        "source_ids": [
          "I06-017"
        ],
        "anchor": "I06-017",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Проверка вероятностей",
        "id": "I06-018",
        "old": "I06",
        "source_ids": [
          "I06-018"
        ],
        "anchor": "I06-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Если среди эпизодов с прогнозом 0,8 событие происходит примерно в 80% случаев, это один из признаков калибровки. Это не гарантирует качество на новой комнате и не означает одинаковую цену всех ошибок. Методы калибровки нейросетей и температурное масштабирование описаны в исследовании Guo и соавторов. [128]",
        "id": "I06-019",
        "old": "I06",
        "source_ids": [
          "I06-019"
        ],
        "anchor": "I06-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для Koobique проверяем калибровку по эпизодам, отдельно для неподвижного человека, пустой комнаты с вентилятором и отказа канала. Соседние секунды одного эпизода не считаем независимыми тестами. Неизвестный режим переводит систему к безопасной для данной нагрузки политике, а не к максимальной уверенности ближайшего класса.",
        "id": "I06-020",
        "old": "I06",
        "source_ids": [
          "I06-020"
        ],
        "anchor": "I06-020",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Погрешность и калибровка",
        "y": 632.6160278320312,
        "page": 136,
        "old": 40,
        "id": "A40-022",
        "source_ids": [
          "A40-022"
        ],
        "anchor": "A40-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Показания двух датчиков 23,4 и 23,7 °C не определяют, какой из них прав. Для оценки смещения нужна опора с известной погрешностью, стабильная среда и достаточная выдержка. Корпус и положение входят в измерительную систему наряду с микросхемой. [15][78]",
        "y": 659.6336669921875,
        "page": 136,
        "old": 40,
        "id": "A40-023",
        "source_ids": [
          "A40-023"
        ],
        "anchor": "A40-023",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Пусть только для расчётного примера независимые стандартные неопределённости трёх вкладов равны 0,10, 0,08 и 0,05 °C. Тогда корень суммы квадратов равен примерно 0,137 °C. Складывать таким способом паспортные максимумы без распределений и предположения независимости некорректно. А общий систематический нагрев на 1 °C не исчезнет от усреднения тысячи отсчётов.",
        "y": 57.42815399169922,
        "page": 137,
        "old": 40,
        "id": "A40-024",
        "source_ids": [
          "A40-024"
        ],
        "anchor": "A40-024",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Практический журнал хранит сырое значение, поправку, версию калибровки, положение и метод. После изменения корпуса проверяется, сохранилась ли поправка. Для lux отдельно проверяются ориентация, материал окна, спектр источника, насыщение и собственный светильник.",
        "y": 145.80709838867188,
        "page": 137,
        "old": 40,
        "id": "A40-025",
        "source_ids": [
          "A40-025"
        ],
        "anchor": "A40-025",
        "content_status": "active"
      },
      {
        "id": "R29-001",
        "kind": "h2",
        "text": "Почему уверенность не должна расти от одинаковых кадров",
        "source_ids": [
          "R29-001"
        ],
        "anchor": "R29-001",
        "content_status": "active"
      },
      {
        "id": "R29-002",
        "kind": "p",
        "text": "Если человек один раз пошевелился, двадцать почти одинаковых кадров радара не являются двадцатью независимыми свидетельствами. Повторное умножение одной likelihood быстро даёт ложную уверенность. Для E0 используется проверяемый конечный автомат, а для вероятностного кандидата — модель переходов состояния и наблюдений, например HMM с предсказанием перед каждым обновлением.",
        "source_ids": [
          "R29-002"
        ],
        "anchor": "R29-002",
        "content_status": "active"
      },
      {
        "id": "R29-003",
        "kind": "p",
        "text": "Корреляцию PIR, радара и движения изображения нельзя исправить произвольным коэффициентом доверия «по вкусу». Допускается исследовать совместную модель либо явно ослабленную likelihood, но параметр оценивается на независимых эпизодах. При неизвестной зависимости система сообщает недостаточность оснований. Логи и метрики программы называем эксплуатационной наблюдаемостью; способность различить физические состояния — наблюдаемостью процесса.",
        "source_ids": [
          "R29-003"
        ],
        "anchor": "R29-003",
        "content_status": "active"
      },
      {
        "id": "R3-29-BOUND-H",
        "kind": "h2",
        "text": "Неопределённость меняет допустимый вывод",
        "source_ids": [],
        "anchor": "R3-29-BOUND-H",
        "content_status": "r3_addition"
      },
      {
        "id": "R3-29-BOUND-P1",
        "kind": "p",
        "text": "Пороговая политика должна указывать, какое свидетельство ей достаточно: одно показание, интервал с заданным смыслом или откалиброванная вероятность. Эти варианты не взаимозаменяемы. Например, условные 300 лк с допустимой ошибкой ±40 лк не доказывают выполнение требования «не ниже 280 лк»: нижняя граница равна 260 лк. Это арифметический пример проверки условия, а не паспортная погрешность выбранного датчика.",
        "source_ids": [],
        "anchor": "R3-29-BOUND-P1",
        "content_status": "r3_addition"
      },
      {
        "id": "R3-29-BOUND-P2",
        "kind": "p",
        "text": "Если для действия нужно доказать выполнение порога, диапазон, пересекающий порог, оставляет решение неопределённым. Процесс может запросить дополнительное измерение, удержать согласованное состояние или предложить человеку уточнение. Нельзя автоматически сужать интервал только увеличением числа соседних отсчётов: общая ошибка размещения или калибровки остаётся общей. Политика фиксирует, какая неопределённость сокращается повтором и какая требует нового опыта.",
        "source_ids": [],
        "anchor": "R3-29-BOUND-P2",
        "content_status": "r3_addition"
      }
    ],
    "origins": [
      "I06",
      [
        "A40",
        22,
        25
      ]
    ],
    "question": "Что означает вероятность на экране?",
    "intro": "Показание сенсора и вероятность присутствия — не одно и то же. Первое связано с измерительным трактом и неопределённостью. Второе получается из модели, её предположений и данных о прошлых случаях.\n\nЧисло 0,87 не становится надёжнее из-за двух знаков после запятой. Сначала выясняем, при каких условиях оно получено и как проверена модель. Когда такой проверки ещё нет, честное описание свидетельств полезнее точного на вид процента.",
    "id": "ch-29",
    "slug": "chapter-29"
  },
  {
    "number": 30,
    "title": "Модель мира без магии",
    "group": "Физическая модель",
    "units": [
      {
        "kind": "lead",
        "text": "Модель мира полезна, если умеет ответить: «Что изменится после моего действия, насколько я этому доверяю и где прогноз перестанет работать?»",
        "id": "I07-001",
        "old": "I07",
        "source_ids": [
          "I07-001"
        ],
        "anchor": "I07-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Состав и назначение модели мира",
        "id": "I07-002",
        "old": "I07",
        "source_ids": [
          "I07-002"
        ],
        "anchor": "I07-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Предлагаем хранить два связанных представления. Первое — явное: помещение, рабочая зона, датчик, светильник, окно, пользовательская цель. Связи имеют смысл: «расположен в», «измеряет», «питается через», «влияет на», «наблюдает результат». Второе — обучаемое: параметры динамики, признаки временного окна, вероятностные оценки и скрытые представления нейросетей.",
        "id": "I07-003",
        "old": "I07",
        "source_ids": [
          "I07-003"
        ],
        "anchor": "I07-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Явная модель нужна для прав, единиц, объяснений и переноса. Обучаемая нужна там, где точная зависимость заранее неизвестна. Нейросетевой вектор не должен быть единственным местом, где хранится факт «это реле управляет настольной лампой».",
        "id": "I07-004",
        "old": "I07",
        "source_ids": [
          "I07-004"
        ],
        "anchor": "I07-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В Brick уже существует семантическое описание оборудования, пространств и отношений здания. [103] Мы можем использовать совместимый экспорт и словарь отношений, но не обязаны запускать отдельную графовую СУБД для первого кабинета. Небольшой типизированный граф в памяти с постоянным хранением достаточно выразителен для начального стенда.",
        "id": "I07-005",
        "old": "I07",
        "source_ids": [
          "I07-005"
        ],
        "anchor": "I07-005",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I07",
        "caption": "Связи различного типа нельзя смешивать. «Реле питает лампу» задаётся монтажом; «лампа повышает освещённость здесь» проверяется измерением; «человек хочет читать» задаётся интерфейсом или интерпретацией запроса.",
        "id": "I07-006",
        "old": "I07",
        "source_ids": [
          "I07-006"
        ],
        "anchor": "I07-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Прогноз без действия и прогноз после действия",
        "id": "I07-007",
        "old": "I07",
        "source_ids": [
          "I07-007"
        ],
        "anchor": "I07-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Пусть KQMATH012TOKEN — скрытое состояние комнаты, KQMATH013TOKEN — действие, KQMATH014TOKEN — внешние воздействия, KQMATH015TOKEN — измерение. Общая заготовка:",
        "id": "I07-008",
        "old": "I07",
        "source_ids": [
          "I07-008"
        ],
        "anchor": "I07-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "KQMATH011TOKEN",
        "id": "I07-009",
        "old": "I07",
        "source_ids": [
          "I07-009"
        ],
        "anchor": "I07-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "KQMATH016TOKEN описывает изменение мира, KQMATH017TOKEN — то, как мы его видим; KQMATH018TOKEN и KQMATH019TOKEN отражают неполноту модели и ошибки наблюдения. Формула не требует нейросети. Для лампы достаточно простого вклада в освещённость; для сложного узла часть KQMATH020TOKEN можно обучить.",
        "id": "I07-010",
        "old": "I07",
        "source_ids": [
          "I07-010"
        ],
        "anchor": "I07-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Прогноз «завтра к девяти будет светлее» не доказывает «если я включу реле, станет светлее на 400 лк». Первый использует статистическую регулярность, второй требует информации о воздействии. Для обучения второго в данных обязательно присутствуют действия, фактическое состояние выхода, внешние условия и результат.",
        "id": "I07-011",
        "old": "I07",
        "source_ids": [
          "I07-011"
        ],
        "anchor": "I07-011",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Причинная ловушка",
        "id": "I07-012",
        "old": "I07",
        "source_ids": [
          "I07-012"
        ],
        "anchor": "I07-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Вечером человек одновременно включает лампу и закрывает дверь. Наблюдательная модель может связать закрытие двери с ростом освещённости. Чтобы отделить зависимости, нужен монтажный смысл, физическое ограничение и безопасные измерения при изменении одного воздействия. Корреляция может помогать прогнозу, но не выдаёт разрешение управлять чужим устройством ради желаемого результата.",
        "id": "I07-013",
        "old": "I07",
        "source_ids": [
          "I07-013"
        ],
        "anchor": "I07-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Не обещаем автоматическое открытие законов физики из нескольких часов телеметрии. Сначала задаём структуру процесса, затем оцениваем неизвестные коэффициенты. Например, знаем, что светильник способен добавлять свет, и оцениваем его вклад в конкретной точке.",
        "id": "I07-014",
        "old": "I07",
        "source_ids": [
          "I07-014"
        ],
        "anchor": "I07-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Область применимости",
        "id": "I07-015",
        "old": "I07",
        "source_ids": [
          "I07-015"
        ],
        "anchor": "I07-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для каждой модели храним диапазоны входов, поддерживаемые размещения, конфигурации оборудования и горизонт прогноза. Перестановка лампы, новая штора или изменение режима радара могут перевести модель в состояние «нужна перепроверка».",
        "id": "I07-016",
        "old": "I07",
        "source_ids": [
          "I07-016"
        ],
        "anchor": "I07-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Выход за область — не обязательно авария. Координация возвращается к более простому правилу, а модель продолжает наблюдать в теневом режиме. Для высокорискового процесса реакция может быть строже и определяется отдельно.",
        "id": "I07-017",
        "old": "I07",
        "source_ids": [
          "I07-017"
        ],
        "anchor": "I07-017",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Конфликт двух моделей",
        "id": "I07-018",
        "old": "I07",
        "source_ids": [
          "I07-018"
        ],
        "anchor": "I07-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Если одна модель говорит «пусто», а другая видит присутствие, нельзя молча усреднить их до удобного числа. Сначала выясняем, одинаковы ли объект, интервал времени и исходные источники. После этого процесс выбирает политику конфликта: собрать ещё данные, удержать состояние или спросить пользователя.",
        "id": "I07-019",
        "old": "I07",
        "source_ids": [
          "I07-019"
        ],
        "anchor": "I07-019",
        "content_status": "active"
      },
      {
        "kind": "quote",
        "text": "Модель мира хранит версии связей, динамику и область применимости прогноза. Для работы нужны явные объекты, действия и границы знания; пространственное изображение комнаты может служить дополнительным способом их изучения.",
        "id": "I07-020",
        "old": "I07",
        "source_ids": [
          "I07-020"
        ],
        "anchor": "I07-020",
        "content_status": "active"
      },
      {
        "id": "R30-001",
        "kind": "h2",
        "text": "Модель объекта — не список названий",
        "source_ids": [
          "R30-001"
        ],
        "anchor": "R30-001",
        "content_status": "active"
      },
      {
        "id": "R30-002",
        "kind": "p",
        "text": "Светильник связан с физическим каналом, комнатой, группами, обратной связью и допустимыми действиями. «Подсветка» и «освещение» могут обозначать разные наборы. Имя — только пользовательский псевдоним; источник истины — ID и текущая версия графа. Перенос между комнатами инвалидирует старые разрешения и модели, где размещение существенно.",
        "source_ids": [
          "R30-002"
        ],
        "anchor": "R30-002",
        "content_status": "active"
      },
      {
        "id": "R30-003",
        "kind": "p",
        "text": "Физическое измерение иногда зависит от действия мгновенно: y = h(x, a), а не только h(x). Для света это прямой вклад собственной лампы; для температуры основной эффект может быть медленным. Такой прямой канал нужно указать в паспорте, иначе оценщик ошибочно объяснит включение лампы изменением внешнего состояния.",
        "source_ids": [
          "R30-003"
        ],
        "anchor": "R30-003",
        "content_status": "active"
      },
      {
        "id": "R3-30-TARGET-H",
        "kind": "h2",
        "text": "Точка датчика и цель рабочего места",
        "source_ids": [],
        "anchor": "R3-30-TARGET-H",
        "content_status": "r3_addition"
      },
      {
        "id": "R3-30-TARGET-P1",
        "kind": "p",
        "text": "Измеряемое место и объект цели задаются раздельно. Если цель относится к рабочей плоскости, сенсор устанавливают в выбранной точке этой плоскости либо проверяют отдельное преобразование от сенсора к целевой зоне. Показание у монитора нельзя без проверки объявить освещённостью всего стола. В паспорте процесса сохраняются target_location_id, ориентация и размещение сенсора, способ сопоставления и область применимости; названия этих полей здесь задают семантику, а не новый бинарный профиль KQIP.",
        "source_ids": [],
        "anchor": "R3-30-TARGET-P1",
        "content_status": "r3_addition"
      },
      {
        "id": "R3-30-TARGET-P2",
        "kind": "p",
        "text": "Связь зависит от источника света. Перенос коэффициента, измеренного для лампы, на дневной свет требует самостоятельного подтверждения. Для первого опыта проще совместить точку измерения с выбранной точкой цели, затем сравнить несколько положений на столе при разных условиях. Монитор, штора и новая лампа входят в условия опыта. Если преобразование не проверено, интерфейс показывает измерение в точке сенсора, а достижение цели для рабочей зоны оставляет неподтверждённым.",
        "source_ids": [],
        "anchor": "R3-30-TARGET-P2",
        "content_status": "r3_addition"
      }
    ],
    "origins": [
      "I07"
    ],
    "question": "Как описать комнату так, чтобы модель можно было заменить?",
    "intro": "Начнём с вещей, а не с нейросети. Есть стол, светильник, реле, окно и измерительная точка. Реле управляет цепью лампы; лампа влияет на свет у стола; окно добавляет меняющийся фон. Эти связи сохраняют смысл даже после замены алгоритма.\n\nМодель динамики добавляет прогноз: что произойдёт после действия и насколько этому прогнозу можно доверять. Она не стирает граф объектов и не превращает совпадение во времени в доказанную причинность.",
    "id": "ch-30",
    "slug": "chapter-30"
  },
  {
    "number": 31,
    "title": "Паспорт физического процесса",
    "group": "Платформа",
    "units": [
      {
        "kind": "lead",
        "text": "Пакет процесса переносит цель вместе с моделью, ограничениями и способом проверки результата. Список устройств и текст запроса дополняют этот контракт, но сами по себе не определяют поведение в новой комнате.",
        "id": "I08-001",
        "old": "I08",
        "source_ids": [
          "I08-001"
        ],
        "anchor": "I08-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Предлагаемый формат Process Package",
        "id": "I08-002",
        "old": "I08",
        "source_ids": [
          "I08-002"
        ],
        "anchor": "I08-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Назовём переносимое описание пакетом процесса. Это собственный контракт Koobique, пока не стандарт и не готовая библиотека. Его читаемая часть объясняет смысл; машинная часть содержит типизированные поля, допустимые значения и ссылки на артефакты. Ниже — состав, который нужен уже для освещения.",
        "id": "I08-003",
        "old": "I08",
        "source_ids": [
          "I08-003"
        ],
        "anchor": "I08-003",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Часть",
            "Пример кабинета"
          ],
          [
            "Объект и цель",
            "Рабочая поверхность; согласованный диапазон света при присутствии"
          ],
          [
            "Входы",
            "Освещённость, оценка присутствия, здоровье каналов, ручной режим"
          ],
          [
            "Исполнители",
            "Конкретная лампа через конкретное реле; только ON/OFF"
          ],
          [
            "Модель",
            "Вклад лампы и остаточная неопределённость; версия размещения"
          ],
          [
            "Ограничения",
            "Минимальная пауза между переключениями, срок команды, права владельца"
          ],
          [
            "Проверка",
            "Выход подтвердил состояние; свет изменился в ожидаемом направлении"
          ],
          [
            "Отказ",
            "Неизвестное присутствие не трактуется как пустота; политика света согласована"
          ],
          [
            "Ресурсы",
            "Максимальный размер состояния, время вычисления, квота истории"
          ],
          [
            "Доказательства",
            "Тестовые эпизоды, условия допуска, известные слабые режимы"
          ]
        ],
        "id": "I08-004",
        "old": "I08",
        "source_ids": [
          "I08-004"
        ],
        "anchor": "I08-004",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Связывание с реальной комнатой",
        "id": "I08-005",
        "old": "I08",
        "source_ids": [
          "I08-005"
        ],
        "anchor": "I08-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Установщик пакета ищет подходящие возможности, но не связывает их автоматически по похожим названиям. Пользователь указывает физический объект и подтверждает нагрузку; тест определяет наблюдаемый эффект. Если лампа умеет только включаться и выключаться, параметр «яркость 37%» не становится доступным действием.",
        "id": "I08-006",
        "old": "I08",
        "source_ids": [
          "I08-006"
        ],
        "anchor": "I08-006",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I08",
        "caption": "Установка процесса напоминает связывание программы с ресурсами. Отсутствующий датчик, неизвестная нагрузка или несовместимая единица останавливают допуск, а не превращаются в заполненный AI параметр.",
        "id": "I08-007",
        "old": "I08",
        "source_ids": [
          "I08-007"
        ],
        "anchor": "I08-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "У пакета есть два результата проверки: структурная совместимость и эксплуатационный допуск. Первая означает, что типы и интерфейсы сходятся. Второй — что выбранная сборка проверена для данного сценария. Нельзя заменить второй результат успешной проверкой JSON Schema.",
        "id": "I08-008",
        "old": "I08",
        "source_ids": [
          "I08-008"
        ],
        "anchor": "I08-008",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Жизненный цикл",
        "id": "I08-009",
        "old": "I08",
        "source_ids": [
          "I08-009"
        ],
        "anchor": "I08-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Предлагаемые состояния: черновик, связан с ресурсами, наблюдает, проверен на записи, ограниченно управляет, действует в подтверждённой области, приостановлен. Переходы журналируются. Подписанный пакет от автора всё равно не получает автоматически право управлять помещением владельца.",
        "id": "I08-010",
        "old": "I08",
        "source_ids": [
          "I08-010"
        ],
        "anchor": "I08-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Обновление пакета сравнивает не только версию кода, но и область полномочий. Добавление нового исполнительного устройства, увеличение диапазона или смена политики отказа требует отдельного разрешения. Исправление текста подсказки не должно проходить тот же тяжёлый путь.",
        "id": "I08-011",
        "old": "I08",
        "source_ids": [
          "I08-011"
        ],
        "anchor": "I08-011",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Ресурс «влияние на среду»",
        "id": "I08-012",
        "old": "I08",
        "source_ids": [
          "I08-012"
        ],
        "anchor": "I08-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Две программы могут одновременно хотеть управлять одним реле. Поэтому пакет запрашивает право на действие через арбитр. Приоритет определяется политикой пользователя и устройством, а не тем, какая модель прислала более высокий confidence.",
        "id": "I08-013",
        "old": "I08",
        "source_ids": [
          "I08-013"
        ],
        "anchor": "I08-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "У одной цели тоже могут быть несколько исполнителей. Освещение комнаты и экрана конфликтуют с экономией энергии; вентиляция конфликтует с шумом. Пакет объявляет целевые величины и цену компромисса, а жёсткие ограничения остаются отдельно. Человек должен понимать, что оптимизируется, а не обнаруживать это по неожиданному поведению системы.",
        "id": "I08-014",
        "old": "I08",
        "source_ids": [
          "I08-014"
        ],
        "anchor": "I08-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Как добавлять новые алгоритмы",
        "id": "I08-015",
        "old": "I08",
        "edited": true,
        "source_ids": [
          "I08-015"
        ],
        "anchor": "I08-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В первой версии пакет — декларативные данные и ссылка на встроенный Rust-алгоритм. Затем можно допустить изолированный вычислительный модуль. Ему выдаются только объявленные входы, бюджет и интерфейс предложений. Доступ к сокетам устройств или файловой системе станции не возникает из самого факта установки.",
        "id": "I08-016",
        "old": "I08",
        "source_ids": [
          "I08-016"
        ],
        "anchor": "I08-016",
        "content_status": "active"
      },
      {
        "id": "N-018",
        "kind": "h2",
        "text": "Пример переноса в другую комнату",
        "new": true,
        "source_ids": [
          "N-018"
        ],
        "anchor": "N-018",
        "content_status": "active"
      },
      {
        "id": "N-019",
        "kind": "p",
        "text": "Пакет освещения знает, что ему нужен измеритель света на рабочей поверхности, оценка присутствия и двоичный исполнитель. В новом кабинете эти типы нашлись — структурная проверка пройдена. Но старая лампа давала в точке датчика один вклад, а новая расположена иначе. Эксплуатационный допуск ещё не получен.",
        "new": true,
        "source_ids": [
          "N-019"
        ],
        "anchor": "N-019",
        "content_status": "active"
      },
      {
        "id": "N-020",
        "kind": "p",
        "text": "Пользователь подтверждает назначение реле и размещение сенсора. Система сначала наблюдает, затем в разрешённом опыте оценивает влияние лампы и проверяет его на отдельных эпизодах. До этого она использует согласованный простой режим. При переносе сохраняется архитектура пакета, но не необоснованная уверенность в старых коэффициентах.",
        "new": true,
        "source_ids": [
          "N-020"
        ],
        "anchor": "N-020",
        "content_status": "active"
      },
      {
        "id": "R31-001",
        "kind": "h2",
        "text": "Пакет процесса в первом экземпляре",
        "source_ids": [
          "R31-001"
        ],
        "anchor": "R31-001",
        "content_status": "active"
      },
      {
        "id": "R31-002",
        "kind": "p",
        "text": "В E0 пакет — статическая проверяемая конфигурация и имя встроенного Rust-автомата. Не требуются магазин модулей, произвольный планировщик, граф распределённого исполнения или нейросеть. При этом сохраняются цель, входы, ограничения, условия допуска и проверка результата; они нужны и для одной лампы, чтобы следующий экземпляр не зависел от памяти автора.",
        "source_ids": [
          "R31-002"
        ],
        "anchor": "R31-002",
        "content_status": "active"
      },
      {
        "id": "R31-003",
        "kind": "p",
        "text": "Для PIR-only пакет не может заявить requires=presence_boolean и получить право автоматического ухода. Он требует конкретный observability_profile и запись допуска на выбранное размещение. При замене радара, перемещении сенсора или восстановлении старого пакета прежний допуск не возобновляется автоматически. Это проверяется до выдачи исполнительных полномочий.",
        "source_ids": [
          "R31-003"
        ],
        "anchor": "R31-003",
        "content_status": "active"
      }
    ],
    "origins": [
      "I08"
    ],
    "question": "Что должно переноситься из одного сценария в другой?",
    "intro": "Повторно использовать нужно не только код правила. Вместе с ним нужны описание измерений, допустимые действия, условия модели, способ проверки результата и поведение при нехватке данных. Мы соберём это в паспорт физического процесса.\n\nТак освещение кабинета станет не набором скрытых настроек, а проверяемым пакетом. При переносе в другую комнату система сможет запросить новое размещение датчика и проверку влияния лампы вместо слепого копирования старых коэффициентов.",
    "id": "ch-31",
    "slug": "chapter-31"
  },
  {
    "number": 32,
    "title": "Как выбрать полезное действие",
    "group": "Решения",
    "units": [
      {
        "kind": "lead",
        "text": "Полезность действия зависит от последствий ошибки, доступных измерений и допустимости ожидания. Самый вероятный ответ модели ещё требуется проверить по этим условиям.",
        "id": "I09-001",
        "old": "I09",
        "source_ids": [
          "I09-001"
        ],
        "anchor": "I09-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "От фразы к проверяемой цели",
        "id": "I09-002",
        "old": "I09",
        "source_ids": [
          "I09-002"
        ],
        "anchor": "I09-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "«Сделай комфортно» недостаточно для автоматического управления. Система должна узнать, о какой величине и зоне идёт речь. Для первого света цель можно выразить так: «когда рабочее место занято, поддерживать не ниже выбранного уровня; не переключать лампу слишком часто; уважать ручное отключение».",
        "id": "I09-003",
        "old": "I09",
        "source_ids": [
          "I09-003"
        ],
        "anchor": "I09-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Не объявляем нормативный уровень освещённости без назначения помещения и нормативного исследования. В примерах книги используются учебные пороги. У пользователя есть возможность изменить профиль, а приложение показывает физическое ограничение: бинарная лампа не удерживает произвольный точный уровень.",
        "id": "I09-004",
        "old": "I09",
        "source_ids": [
          "I09-004"
        ],
        "anchor": "I09-004",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Замкнутый набор вариантов",
        "id": "I09-005",
        "old": "I09",
        "source_ids": [
          "I09-005"
        ],
        "anchor": "I09-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Планировщик получает не универсальное право «управлять домом», а набор допустимых предложений. Для кабинета это может быть: включить основную лампу, выключить её, сохранить состояние, запросить измерение, уточнить цель или отказаться. Само множество вариантов формируется кодом по текущим возможностям и правам.",
        "id": "I09-006",
        "old": "I09",
        "source_ids": [
          "I09-006"
        ],
        "anchor": "I09-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Предложение включает объект, действие, параметры, ожидаемый эффект, неопределённость, срок действия, входной снимок и причину. Исполнитель повторно проверяет условия непосредственно перед отправкой. Между расчётом и действием человек мог нажать кнопку.",
        "id": "I09-007",
        "old": "I09",
        "source_ids": [
          "I09-007"
        ],
        "anchor": "I09-007",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I09",
        "caption": "Разделяем желание, оптимизацию и разрешение. Запрет не является ещё одним мягким штрафом, который модель может «перевесить» выгодой.",
        "id": "I09-008",
        "old": "I09",
        "source_ids": [
          "I09-008"
        ],
        "anchor": "I09-008",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Ожидаемые потери",
        "id": "I09-009",
        "old": "I09",
        "source_ids": [
          "I09-009"
        ],
        "anchor": "I09-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В простом варианте выбираем действие, минимизирующее ожидаемую потерю:",
        "id": "I09-010",
        "old": "I09",
        "source_ids": [
          "I09-010"
        ],
        "anchor": "I09-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "KQMATH021TOKEN",
        "id": "I09-011",
        "old": "I09",
        "source_ids": [
          "I09-011"
        ],
        "anchor": "I09-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "KQMATH022TOKEN — возможное состояние, KQMATH023TOKEN — наблюдения, KQMATH024TOKEN — согласованная цена результата. Если выключить свет при человеке гораздо хуже, чем ненадолго оставить его в пустой комнате, порог выключения должен быть строже порога включения. Это не универсальная моральная шкала, а настройка конкретного безопасного сценария.",
        "id": "I09-012",
        "old": "I09",
        "source_ids": [
          "I09-012"
        ],
        "anchor": "I09-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для неизвестной нагрузки «оставить включённой» не является универсальным безопасным выбором. Сначала определяем её класс, локальные ограничения и поведение при отказе. Нагреватель, насос и светильник требуют разных политик.",
        "id": "I09-013",
        "old": "I09",
        "source_ids": [
          "I09-013"
        ],
        "anchor": "I09-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Неполный и невозможный план",
        "id": "I09-014",
        "old": "I09",
        "source_ids": [
          "I09-014"
        ],
        "anchor": "I09-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Когда данные недостаточны, система может выбрать измерение или вопрос. Когда цель физически недостижима доступными устройствами, она должна сказать об этом. Например, одна включённая лампа даёт слишком мало света: многократная отправка ON не исправляет её мощность.",
        "id": "I09-015",
        "old": "I09",
        "source_ids": [
          "I09-015"
        ],
        "anchor": "I09-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Если пользователь просит включить две лампы, операция не обязана быть физически атомарной. План хранит результат каждой команды. При частичном успехе интерфейс сообщает, что сработало и что осталось неизвестным. Компенсация — отдельное действие, не обещание отката физического времени.",
        "id": "I09-016",
        "old": "I09",
        "source_ids": [
          "I09-016"
        ],
        "anchor": "I09-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Предложение должно быть объяснимым без рассказа модели",
        "id": "I09-017",
        "old": "I09",
        "source_ids": [
          "I09-017"
        ],
        "anchor": "I09-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Основание решения строим из структурных полей: цель, проверенные наблюдения, активные ограничения и выбранный вариант. Языковая модель может переформулировать это для пользователя, но не дописывать несуществующее измерение или эксперимент.",
        "id": "I09-018",
        "old": "I09",
        "source_ids": [
          "I09-018"
        ],
        "anchor": "I09-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Пример объяснения «Включил основную лампу: рабочее место считается занятым, измерено 160 лк, для этого профиля выбран минимум 280 лк. Ручной запрет не активен. Реле подтвердило включение; датчик показал рост света. Точное достижение цели будет проверено следующим измерением».",
        "id": "I09-019",
        "old": "I09",
        "source_ids": [
          "I09-019"
        ],
        "anchor": "I09-019",
        "content_status": "active"
      },
      {
        "kind": "quote",
        "text": "Варианты «измерить», «уточнить», «подождать» и «невозможно» — полноценные решения. Их отсутствие заставляет даже хорошую модель выбирать действие, которому не хватает оснований.",
        "id": "I09-020",
        "old": "I09",
        "source_ids": [
          "I09-020"
        ],
        "anchor": "I09-020",
        "content_status": "active"
      },
      {
        "id": "N-021",
        "kind": "h2",
        "text": "Два вида ограничений на одном примере",
        "new": true,
        "source_ids": [
          "N-021"
        ],
        "anchor": "N-021",
        "content_status": "active"
      },
      {
        "id": "N-022",
        "kind": "p",
        "text": "Пусть пользователь хочет светлее, а модель предлагает включить обе лампы. Запрет гостю управлять второй комнатой — жёсткое ограничение: такое действие вообще не участвует в выборе. Экономия нескольких минут работы доступной лампы — предпочтение: его можно сравнивать с удобством, если владелец задал этот компромисс.",
        "new": true,
        "source_ids": [
          "N-022"
        ],
        "anchor": "N-022",
        "content_status": "active"
      },
      {
        "id": "N-023",
        "kind": "p",
        "text": "Смешивать эти вещи в одном штрафе опасно для смысла системы. Очень большая ожидаемая полезность не должна «перевесить» отсутствие прав, истёкший срок или аппаратный предел. Сначала код строит множество разрешённых действий; затем регулятор или модель выбирает внутри него.",
        "new": true,
        "source_ids": [
          "N-023"
        ],
        "anchor": "N-023",
        "content_status": "active"
      },
      {
        "id": "R32-001",
        "kind": "h2",
        "text": "Один порог стоимости не создаёт гистерезис",
        "source_ids": [
          "R32-001"
        ],
        "anchor": "R32-001",
        "content_status": "active"
      },
      {
        "id": "R32-002",
        "kind": "p",
        "text": "Если сравнивать только ожидаемый ущерб от ON и OFF в одном независимом шаге, обычно получится один порог вероятности, а не два. Гистерезис появляется из памяти состояния, стоимости переключения или явно заданных ограничений dwell. В модели указываем цену недостаточного света за единицу времени, цену лишнего включения за единицу времени и отдельную цену перехода. Иначе частота пересчёта незаметно меняет смысл предпочтений.",
        "source_ids": [
          "R32-002"
        ],
        "anchor": "R32-002",
        "content_status": "active"
      },
      {
        "id": "R32-003",
        "kind": "p",
        "text": "Жёсткие права и пределы безопасности отсекают недопустимые действия до сравнения полезности. Неизвестное состояние не обязано вести к оптимистичному выбору: доступны «оставить», «спросить» и «измерить». Команда из общего микрофона не получает приоритет владельца из-за высокого confidence.",
        "source_ids": [
          "R32-003"
        ],
        "anchor": "R32-003",
        "content_status": "active"
      }
    ],
    "origins": [
      "I09"
    ],
    "question": "Почему правильное решение иногда ничего не включает?",
    "intro": "Система должна выбирать не только между ON и OFF. Она может подождать, запросить ещё одно измерение, уточнить объект или сказать, что доступным исполнительным устройством цель недостижима.\n\nЭто особенно важно при нескольких источниках намерения. Ручное действие человека, автоматический режим и исследовательский план не складываются арифметически. Сначала определяются права и запреты, затем среди допустимых вариантов выбирается полезное действие.",
    "id": "ch-32",
    "slug": "chapter-32"
  },
  {
    "number": 33,
    "title": "Регулятор: от правила к прогнозу",
    "group": "Управление",
    "units": [
      {
        "kind": "lead",
        "text": "Для Koobique понимание физического процесса выражается в проверяемой модели с указанной областью применимости. Прогноз за её пределами требует дополнительных оснований и ограничений.",
        "y": 168.675537109375,
        "page": 82,
        "old": 24,
        "id": "A24-001",
        "source_ids": [
          "A24-001"
        ],
        "anchor": "A24-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Koobique должен уметь связывать измерения и действия. Для этого не нужна сразу цифровая копия всего здания. Нужны небольшие модели: вклад светильника в lux, тепловая инерция комнаты, оценка присутствия, ограничение длительности включения и проверка обратной связи.",
        "y": 219.78143310546875,
        "page": 82,
        "old": 24,
        "id": "A24-002",
        "source_ids": [
          "A24-002"
        ],
        "anchor": "A24-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Четыре уровня интеллекта",
        "y": 299.5755920410156,
        "page": 82,
        "old": 24,
        "id": "A24-003",
        "source_ids": [
          "A24-003"
        ],
        "anchor": "A24-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первый уровень — пороговые правила с гистерезисом и выдержкой. Второй — явные автоматы состояний. Третий — оценивание параметров простых физических моделей. Четвёртый — планирование с несколькими целями и ограничениями. Языковая модель может помогать объяснению и формированию предложения, но не заменяет ни единицы, ни ограничения выхода.",
        "y": 326.5932312011719,
        "page": 82,
        "old": 24,
        "id": "A24-004",
        "source_ids": [
          "A24-004"
        ],
        "anchor": "A24-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первого кабинета достаточно первых двух уровней и нескольких исследовательских моделей третьего. Это не тупиковая архитектура: данные и версии моделей уже подготовлены к усложнению.",
        "y": 414.97216796875,
        "page": 82,
        "old": 24,
        "id": "A24-005",
        "source_ids": [
          "A24-005"
        ],
        "anchor": "A24-005",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Свет как сумма вкладов",
        "y": 478.7913513183594,
        "page": 82,
        "old": 24,
        "id": "A24-006",
        "source_ids": [
          "A24-006"
        ],
        "anchor": "A24-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Простая модель: E = Eday + k₁·u₁ + k₂·u₂ + ε, где E — lux в точке датчика, Eday — дневной вклад, u — состояние или уровень светильника, k — его измеренный вклад, ε — ошибка. Коэффициент зависит от позиции, отражений и настройки источника.",
        "y": 505.80902099609375,
        "page": 82,
        "old": 24,
        "id": "A24-007",
        "source_ids": [
          "A24-007"
        ],
        "anchor": "A24-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для реле u равно 0 или 1. Для диммируемой нагрузки зависимость часто нелинейна, поэтому k·u является лишь приближением в проверенном диапазоне. Перестановка монитора или сенсора меняет модель. Если оценка дневного света ненадёжна, автоматизация должна сохранять безопасное понятное поведение, а не слепо вычитать старый коэффициент.",
        "y": 562.2379150390625,
        "page": 82,
        "old": 24,
        "id": "A24-008",
        "source_ids": [
          "A24-008"
        ],
        "anchor": "A24-008",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Температура как система с инерцией",
        "y": 56.314456939697266,
        "page": 83,
        "old": 24,
        "id": "A24-009",
        "source_ids": [
          "A24-009"
        ],
        "anchor": "A24-009",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A31",
        "caption": "Однозонная тепловая модель. Она полезна для изучения динамики, но не описывает все градиенты и источники тепла кабинета.",
        "y": 91.10079193115234,
        "page": 83,
        "old": 24,
        "id": "A24-010",
        "source_ids": [
          "A24-010"
        ],
        "anchor": "A24-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Уравнение C·dT/dt = P − H·(T − Tb) связывает эффективную теплоёмкость C, тепловую связь H, граничную температуру Tb и суммарный тепловой вход P. Tb может описывать коридор, соседние зоны и наружную среду через выбранную модель, а не обязательно уличный датчик. По пассивной записи при неизвестном P нельзя раздельно определить все C, H и P: это ограничение наблюдаемого эксперимента, а не недостаток оптимизатора. C представляет эффективную теплоёмкость всей выбранной зоны, включая вовлечённые в процесс массы, а не только находящийся в ней воздух. Открытое окно меняет воздухообмен и параметры модели; прежняя идентификация должна пройти проверку применимости в новых условиях.",
        "y": 418.71917724609375,
        "page": 83,
        "old": 24,
        "id": "A24-011",
        "source_ids": [
          "A24-011"
        ],
        "anchor": "A24-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для постоянных параметров характерное время равно τ = C/H. Если условно C = 300 кДж/К и H = 100 Вт/К, τ = 3000 с, то есть 50 минут. Это вычислительный пример, не измеренные свойства кабинета. Он объясняет, почему опрос 100 раз в секунду не делает тепловое управление быстрее.",
        "y": 491.12310791015625,
        "page": 83,
        "old": 24,
        "id": "A24-012",
        "source_ids": [
          "A24-012"
        ],
        "anchor": "A24-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Дискретизация должна учитывать шаг. Грубый шаг может исказить динамику; чрезмерно малый добавит вычисления без новой информации. Для модели первого порядка при постоянных входах удобен точный экспоненциальный переход, а не только явный Euler.",
        "y": 563.5270385742188,
        "page": 83,
        "old": 24,
        "id": "A24-013",
        "source_ids": [
          "A24-013"
        ],
        "anchor": "A24-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Фильтр и задержка",
        "y": 643.3212890625,
        "page": 83,
        "old": 24,
        "id": "A24-014",
        "source_ids": [
          "A24-014"
        ],
        "anchor": "A24-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Экспоненциальное сглаживание yₙ = αxₙ + (1−α)yₙ₋₁ уменьшает разброс, но создаёт запаздывание. При нерегулярных данных α лучше связывать с реальным Δt и выбранной постоянной времени, а не считать одинаковым для любого интервала. Слишком медленный фильтр может скрыть быстрый отказ.",
        "y": 670.3388671875,
        "page": 83,
        "old": 24,
        "id": "A24-015",
        "source_ids": [
          "A24-015"
        ],
        "anchor": "A24-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Фильтрация и safety-проверка разделяются. Невозможная температура или пропавший сенсор должны вызывать диагностическое состояние независимо от гладкой линии на графике. Raw остаётся доступным для исследования.",
        "y": 57.42815399169922,
        "page": 84,
        "old": 24,
        "id": "A24-016",
        "source_ids": [
          "A24-016"
        ],
        "anchor": "A24-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Гистерезис, выдержка и ручной режим",
        "y": 121.24732971191406,
        "page": 84,
        "old": 24,
        "id": "A24-017",
        "source_ids": [
          "A24-017"
        ],
        "anchor": "A24-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для релейного управления задаются два порога и минимальное время включения/выключения. Это уменьшает частое переключение при шуме. Но для освещения гистерезис не заменяет модель собственного влияния лампы, а для температуры не заменяет независимое ограничение нагревателя. Помимо гистерезиса отдельного правила нужен общий предел переходов выбранного реле и нагрузки, действующий для всех новых команд. Числа и место последнего guard у владельца GPIO фиксируются в аппаратном профиле по данным деталей и испытанию. Для лабораторного ограничения в Core явно указывается эта доверительная граница. Ручной OFF учебной лампы не задерживается симметричным cooldown; ограничение следующего ON сохраняет исходный deadline и ручные ревизии. Пока профиль не выбран и guard не проверен, предел физических переключений остаётся открытым условием постоянной эксплуатации.",
        "y": 148.26495361328125,
        "page": 84,
        "old": 24,
        "id": "A24-018",
        "source_ids": [
          "A24-018"
        ],
        "anchor": "A24-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "ManualOverride блокирует обычную автоматику до явного освобождения в E0; временный lease — отдельное расширение E1. Освобождение ведёт к пересчёту по свежим данным, а не к исполнению очереди старых действий. Арбитр выбирает одно намерение, но локальные ограничения MCU сохраняют силу.",
        "y": 220.66888427734375,
        "page": 84,
        "old": 24,
        "id": "A24-019",
        "source_ids": [
          "A24-019"
        ],
        "anchor": "A24-019",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "PID и более сложное управление",
        "y": 300.46307373046875,
        "page": 84,
        "old": 24,
        "id": "A24-020",
        "source_ids": [
          "A24-020"
        ],
        "anchor": "A24-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "PID имеет смысл для непрерывного регулируемого исполнительного механизма и измеряемой динамики. Для механического реле потребуются ограничения частоты, а для нагревателя — отдельная безопасность. Антиwindup нужен, когда интегральная часть продолжает расти при насыщенном выходе. Производную обычно фильтруют из-за чувствительности к шуму.",
        "y": 327.480712890625,
        "page": 84,
        "old": 24,
        "id": "A24-021",
        "source_ids": [
          "A24-021"
        ],
        "anchor": "A24-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первый световой сценарий проверяется конечным автоматом. PID можно отдельно исследовать с безопасной низковольтной нагрузкой и симулятором, если задача требует регулирования. Материалы MIT по обратной связи объясняют устойчивость, переходный процесс и цену задержки. [69]",
        "y": 415.85968017578125,
        "page": 84,
        "old": 24,
        "id": "A24-022",
        "source_ids": [
          "A24-022"
        ],
        "anchor": "A24-022",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Контроль и предложение — разные режимы",
        "y": 495.6538391113281,
        "page": 84,
        "old": 24,
        "id": "A24-023",
        "source_ids": [
          "A24-023"
        ],
        "anchor": "A24-023",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Новая модель сначала работает в shadow mode: рассчитывает действия, но не отправляет их. Результаты сравниваются с действующей политикой и размеченными событиями. Затем включается ограниченный автоматический режим. Любое изменение цели или ограничений имеет версию и возможность отката.",
        "y": 522.6714477539062,
        "page": 84,
        "old": 24,
        "id": "A24-024",
        "source_ids": [
          "A24-024"
        ],
        "anchor": "A24-024",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Эксперимент физической модели",
        "y": 611.3452758789062,
        "page": 84,
        "old": 24,
        "id": "A24-025",
        "source_ids": [
          "A24-025"
        ],
        "anchor": "A24-025",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Измерить освещённость до и после включения одной лампы при нескольких положениях датчика. Оценить её вклад и разброс. Переместить монитор, повторить опыт и увидеть, почему коэффициент модели должен быть связан с размещением, а не считаться постоянным свойством лампы.",
        "y": 629.8064575195312,
        "page": 84,
        "old": 24,
        "id": "A24-026",
        "source_ids": [
          "A24-026"
        ],
        "anchor": "A24-026",
        "content_status": "active"
      },
      {
        "kind": "lead",
        "text": "Для простого реле нужен хороший автомат. Для динамики — оценщик и модель. Нейросеть добавляется, когда её преимущество можно измерить.",
        "id": "I10-001",
        "old": "I10",
        "source_ids": [
          "I10-001"
        ],
        "anchor": "I10-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Лестница сложности",
        "id": "I10-002",
        "old": "I10",
        "source_ids": [
          "I10-002"
        ],
        "anchor": "I10-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Автомат состояний хранит текущий режим и разрешённые переходы. Он подходит для ручного приоритета, тайм-аутов, восстановления и простого света. Легко проверяется перебором событий, но сам не оценивает неизвестную физическую динамику.",
        "id": "I10-003",
        "old": "I10",
        "source_ids": [
          "I10-003"
        ],
        "anchor": "I10-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Оценщик состояния объединяет модель изменения и измерения. Фильтр Калмана полезен при соответствующих линейных предпосылках; нелинейные варианты требуют большего внимания к модели и распределениям. Для присутствия часто удобнее дискретная скрытая модель или конечный автомат, чем формально сложный непрерывный фильтр.",
        "id": "I10-004",
        "old": "I10",
        "source_ids": [
          "I10-004"
        ],
        "anchor": "I10-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Регулятор с прогнозом, MPC, сравнивает несколько будущих последовательностей действий на горизонте, выполняет только первый шаг и пересчитывает план после нового наблюдения. Приближённая модель и ограничения важнее названия алгоритма. Практические материалы по идентификации и робастному управлению дают основу для такого подхода. [110] [111]",
        "id": "I10-005",
        "old": "I10",
        "source_ids": [
          "I10-005"
        ],
        "anchor": "I10-005",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I10",
        "caption": "MPC пересчитывает план, а не доверяет одной траектории до конца. Заштрихованная область обозначает неопределённость прогноза, а не гарантированную границу безопасности.",
        "id": "I10-006",
        "old": "I10",
        "source_ids": [
          "I10-006"
        ],
        "anchor": "I10-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Пример целевой функции",
        "id": "I10-007",
        "old": "I10",
        "source_ids": [
          "I10-007"
        ],
        "anchor": "I10-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для диммируемого низковольтного светильника можно предложить:",
        "id": "I10-008",
        "old": "I10",
        "source_ids": [
          "I10-008"
        ],
        "anchor": "I10-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "KQMATH025TOKEN",
        "id": "I10-009",
        "old": "I10",
        "source_ids": [
          "I10-009"
        ],
        "anchor": "I10-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Здесь первое слагаемое штрафует недостаток света, второе — энергию, третье — резкие изменения. Единицы и масштабы нормируем явно: нельзя складывать люксы и ватты с произвольными коэффициентами и затем называть результат объективным комфортом. Жёсткие ограничения на диапазон, перегрев и права не заменяем штрафами.",
        "id": "I10-010",
        "old": "I10",
        "source_ids": [
          "I10-010"
        ],
        "anchor": "I10-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для бинарного реле KQMATH026TOKEN задача меняется. На коротком горизонте можно перебрать небольшой набор допустимых последовательностей с минимальным временем включения/выключения. При большом горизонте нужны другие методы, включая смешанно-целочисленную оптимизацию. OSQP решает выпуклые квадратичные задачи с вещественными переменными; бинарное реле нельзя просто выдать за поддерживаемую целочисленную переменную. [113]",
        "id": "I10-011",
        "old": "I10",
        "source_ids": [
          "I10-011"
        ],
        "anchor": "I10-011",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Когда MPC избыточен",
        "id": "I10-012",
        "old": "I10",
        "source_ids": [
          "I10-012"
        ],
        "anchor": "I10-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Если лампа имеет одно состояние ON и стабильный вклад в свет, автомат с моделью фона, гистерезисом и паузой между переключениями может быть проще и не хуже. Мы обязаны сравнивать AI не с заведомо сломанным правилом, а с таким разумным эталоном.",
        "id": "I10-013",
        "old": "I10",
        "source_ids": [
          "I10-013"
        ],
        "anchor": "I10-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для медленной температуры прогноз может принести больше пользы: он учитывает инерцию и ожидаемое изменение условий. Но первый релейный стенд не подключается к нагревателю ради обучения. Температурный процесс сначала изучаем пассивно и в симуляции.",
        "id": "I10-014",
        "old": "I10",
        "source_ids": [
          "I10-014"
        ],
        "anchor": "I10-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Робастность и ограничения",
        "id": "I10-015",
        "old": "I10",
        "source_ids": [
          "I10-015"
        ],
        "anchor": "I10-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Робастный подход рассматривает диапазон возможной ошибки модели; вероятностный — допускает описанную вероятность нарушения при определённых предпосылках. Эти предпосылки требуют проверки. Слишком широкая неопределённость может сделать задачу неразрешимой; правильный ответ тогда — отказ от плана, а не скрытое ослабление ограничений.",
        "id": "I10-016",
        "old": "I10",
        "source_ids": [
          "I10-016"
        ],
        "anchor": "I10-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Control Barrier Functions позволяют формулировать условия сохранения безопасного множества в рамках заданной динамики и доступного управления. Это серьёзный математический инструмент, но не универсальная защита от неправильной модели, задержки сети или залипшего контакта. [114] Для первого кабинета независимые ограничения и локальная безопасная логика важнее формальной терминологии.",
        "id": "I10-017",
        "old": "I10",
        "source_ids": [
          "I10-017"
        ],
        "anchor": "I10-017",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Практическая траектория",
        "id": "I10-018",
        "old": "I10",
        "source_ids": [
          "I10-018"
        ],
        "anchor": "I10-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сначала реализуем читаемый эталон в симуляторе. Затем идентифицируем небольшую модель по разрешённому эксперименту. Проверяем прогноз на отложенных эпизодах. Только после этого сравниваем MPC, гибридную модель и более сложную политику. Для исследования доступны do-mpc и OSQP; их наличие не означает, что Python становится обязательной средой на станции. [112] [113]",
        "id": "I10-019",
        "old": "I10",
        "source_ids": [
          "I10-019"
        ],
        "anchor": "I10-019",
        "content_status": "active"
      },
      {
        "id": "R33-001",
        "kind": "h2",
        "text": "Какие параметры температуры действительно можно узнать",
        "source_ids": [
          "R33-001"
        ],
        "anchor": "R33-001",
        "content_status": "active"
      },
      {
        "id": "R33-002",
        "kind": "p",
        "text": "Если одновременно умножить C, H и неизвестный P на одно положительное число, уравнение даст ту же температурную траекторию. Поэтому из пассивной записи можно исследовать τ=C/H и равновесный сдвиг P/H, но не приписывать каждому параметру единственное физическое значение. Известный тепловой вход или дополнительные измерения меняют постановку; ради этого первый стенд не подключается к опасному нагревателю.",
        "source_ids": [
          "R33-002"
        ],
        "anchor": "R33-002",
        "content_status": "active"
      },
      {
        "id": "R33-003",
        "kind": "code",
        "text": "T_next = Tb + P/H + (T_now − Tb − P/H) · exp(−Δt/τ)\nτ = C/H     (постоянные P, H и Tb на шаге)",
        "source_ids": [
          "R33-003"
        ],
        "anchor": "R33-003",
        "content_status": "active"
      },
      {
        "id": "R33-004",
        "kind": "p",
        "text": "Это точная дискретизация выбранной однозонной модели, не точная модель любой комнаты. Пример C=300 кДж/К и H=100 Вт/К сохраняет учебный смысл, но не выдаётся за идентифицированную теплоёмкость кабинета. Если наблюдаются быстрый воздушный и медленный строительный режимы, сравниваем 2R2C или другую модель на независимых эпизодах и штрафуем лишнюю сложность.",
        "source_ids": [
          "R33-004"
        ],
        "anchor": "R33-004",
        "content_status": "active"
      },
      {
        "id": "R33-005",
        "kind": "h2",
        "text": "Прогноз и исполнитель должны совпадать по возможностям",
        "source_ids": [
          "R33-005"
        ],
        "anchor": "R33-005",
        "content_status": "active"
      },
      {
        "id": "R33-006",
        "kind": "p",
        "text": "Для реле с двумя состояниями непрерывное решение 0,37 не исполняется как «37%». Варианты — дискретное планирование или временное пропорционирование с минимальным временем ON/OFF и проверенным ресурсом контактов; для света часто лучше хороший автомат. PID и MPC не снимают необходимость аппаратного ограничения опасной нагрузки.",
        "source_ids": [
          "R33-006"
        ],
        "anchor": "R33-006",
        "content_status": "active"
      },
      {
        "id": "R33-007",
        "kind": "p",
        "text": "В целевой функции освещения учитываем присутствие, допустимый верхний световой предел и стоимость переключений. Квадрат положительной части дефицита для QP-солвера записывается через дополнительную неотрицательную переменную с ограничением, а не передаётся как произвольная нелинейная функция. Терминальная стоимость/ограничения выбираются под задачу; учебная сумма на горизонте сама не доказывает устойчивость.",
        "source_ids": [
          "R33-007"
        ],
        "anchor": "R33-007",
        "content_status": "active"
      },
      {
        "id": "R33-008",
        "kind": "h2",
        "text": "Теневой режим не измеряет неслучившийся свет",
        "source_ids": [
          "R33-008"
        ],
        "anchor": "R33-008",
        "content_status": "active"
      },
      {
        "id": "R33-009",
        "kind": "p",
        "text": "Без вмешательства можно честно измерить время вычисления, расхождение с действующей политикой, нарушение правил допуска и совпадение с разметкой намерения. Время ON, комфорт и энергия альтернативной политики — контрфактические величины. Для них требуется модель среды с оговорённой ошибкой либо разрешённый эксперимент/A/B. Replay не создаёт настоящих последствий другой команды.",
        "source_ids": [
          "R33-009"
        ],
        "anchor": "R33-009",
        "content_status": "active"
      }
    ],
    "origins": [
      "A24",
      "I10"
    ],
    "question": "Когда достаточно правила, а когда нужна модель?",
    "intro": "Для одной лампы конечный автомат часто оказывается лучшей первой системой. Он понятен и легко проверяется. Модель становится полезной, когда нужно учитывать собственный вклад лампы, предсказывать медленный процесс или выбирать действие с отложенным результатом.\n\nВ этой главе постепенно перейдём от порогов к обратной связи и прогнозному управлению. Каждый следующий метод должен решать конкретную проблему предыдущего, а не просто выглядеть более современным.",
    "id": "ch-33",
    "slug": "chapter-33"
  },
  {
    "number": 34,
    "title": "Jev: решение с заданным типом",
    "group": "AI",
    "units": [
      {
        "kind": "lead",
        "text": "Jev и JEPA — разные технологии. Для Koobique интересна идея маленьких типизированных решений; конкретная модель оценивается по автономности и нашим тестам.",
        "id": "I11-001",
        "old": "I11",
        "source_ids": [
          "I11-001"
        ],
        "anchor": "I11-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Что подтверждено источниками",
        "id": "I11-002",
        "old": "I11",
        "source_ids": [
          "I11-002"
        ],
        "anchor": "I11-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "TypeSafe представила Jev как модель семейства System One: вместо свободного текста она выдаёт типизированные решения. Основные формы — выбор из вариантов, оценка по шкале и двоичное суждение. Код приложения задаёт вопрос и допустимый ответ, а затем определяет дальнейший ход работы. [115]",
        "id": "I11-003",
        "old": "I11",
        "source_ids": [
          "I11-003"
        ],
        "anchor": "I11-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "На дату проверки, 15 октября 2026 года, официальная страница моделей различает версии и псевдонимы. В выпуске от 14 октября 2026 года объявлена Jev 1.14.0; изображения, увеличенный контекст и тип Box описаны как экспериментальные возможности. Поэтому было бы неверно считать семейство безусловно только текстовым. Для инженерного решения нужно фиксировать конкретную версию и профиль, а не ссылаться на постоянно меняющийся latest. [116] [120]",
        "id": "I11-004",
        "old": "I11",
        "source_ids": [
          "I11-004"
        ],
        "anchor": "I11-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В изученных публичных материалах подтверждён доступ через API. Проверенного способа скачать веса и установить Jev автономно на Koobique не найдено. Это не утверждение о невозможности отдельной договорённости с поставщиком; это отсутствие подтверждения для обязательной локальной основы проекта. [116]",
        "id": "I11-005",
        "old": "I11",
        "source_ids": [
          "I11-005"
        ],
        "anchor": "I11-005",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Что можно заимствовать без зависимости от сервиса",
        "id": "I11-006",
        "old": "I11",
        "source_ids": [
          "I11-006"
        ],
        "anchor": "I11-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Полезный паттерн — задать несколько коротких смысловых вопросов к одному снимку: тип запроса, помещение, группа света, действие, необходимость уточнения. В smart-home demo TypeSafe показывает параллельную оценку таких вопросов, после которой код отбрасывает нерелевантные ответы. [119]",
        "id": "I11-007",
        "old": "I11",
        "source_ids": [
          "I11-007"
        ],
        "anchor": "I11-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В Koobique аналогичный контракт может выполнять локальный классификатор, embedding-модель с проверенными примерами, небольшая генеративная модель со строгим выводом или комбинация. Это собственный сервис типизированных решений, а не обещание воспроизвести качество Jev без её обучения.",
        "id": "I11-008",
        "old": "I11",
        "source_ids": [
          "I11-008"
        ],
        "anchor": "I11-008",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I11",
        "caption": "Типизированный ответ ограничивает форму. Правильность смысла проверяется на данных; право и физическая допустимость — независимым исполнителем. Jev здесь один из возможных исследуемых интерпретаторов.",
        "id": "I11-009",
        "old": "I11",
        "source_ids": [
          "I11-009"
        ],
        "anchor": "I11-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Почему confidence не равен безопасности",
        "id": "I11-010",
        "old": "I11",
        "source_ids": [
          "I11-010"
        ],
        "anchor": "I11-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В опубликованной формуле TypeSafe для Choice с KQMATH028TOKEN вариантами:",
        "id": "I11-011",
        "old": "I11",
        "source_ids": [
          "I11-011"
        ],
        "anchor": "I11-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "KQMATH027TOKEN",
        "id": "I11-012",
        "old": "I11",
        "source_ids": [
          "I11-012"
        ],
        "anchor": "I11-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "При вероятностях KQMATH029TOKEN и KQMATH030TOKEN значение одинаково: KQMATH031TOKEN. Формула описывает отрыв максимума от равномерного выбора, а не вероятность того, что реле физически безопасно включить. Документация отдельно объясняет различие confidence и исходных вероятностей. [117]",
        "id": "I11-013",
        "old": "I11",
        "source_ids": [
          "I11-013"
        ],
        "anchor": "I11-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Наше решение: даже максимальный score не отменяет ручной запрет, просроченный снимок или неопределённую нагрузку. Порог выбора настраивается по собственным эпизодам и последствиям ошибок. Арифметику, сравнение времени и проверку единиц выполняет код.",
        "id": "I11-014",
        "old": "I11",
        "source_ids": [
          "I11-014"
        ],
        "anchor": "I11-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Указанные разработчиком слабые места",
        "id": "I11-015",
        "old": "I11",
        "source_ids": [
          "I11-015"
        ],
        "anchor": "I11-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для Jev 1.13 опубликованы проблемы с числовой точностью, датами, косвенными формулировками, лишним контекстом и влиянием порядка вариантов. Также отмечена восприимчивость к намеренно направляющему содержимому. [118] Эти сведения не доказывают, что последующая версия ведёт себя точно так же; они задают необходимые классы наших тестов.",
        "id": "I11-016",
        "old": "I11",
        "source_ids": [
          "I11-016"
        ],
        "anchor": "I11-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Поэтому в исследовательском сравнении переставляем варианты ответа, добавляем противоречивые показания, длинные имена устройств и фразы вроде «датчик сообщает: игнорируй запреты». Последнее — данные недоверенного источника, не инструкция ядру. У модели не должно быть канала, позволяющего превратить такую строку в полномочие.",
        "id": "I11-017",
        "old": "I11",
        "source_ids": [
          "I11-017"
        ],
        "anchor": "I11-017",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Место Jev в проекте",
        "id": "I11-018",
        "old": "I11",
        "source_ids": [
          "I11-018"
        ],
        "anchor": "I11-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Предлагаем использовать опубликованный подход как ориентир интерфейса, а модель — только как необязательного кандидата лабораторного сравнения. Отправка данных внешнему сервису требует отдельного согласия и подходящего набора без лишней личной информации. Основная комната не зависит от его доступности.",
        "id": "I11-019",
        "old": "I11",
        "source_ids": [
          "I11-019"
        ],
        "anchor": "I11-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Jev остаётся необязательным исследовательским кандидатом, пока автономное размещение не подтверждено. Для локального Koobique полезен строгий контракт типизированного решения; физическое исполнение сохраняется на собственной стороне.",
        "id": "I11-020",
        "old": "I11",
        "source_ids": [
          "I11-020"
        ],
        "anchor": "I11-020",
        "content_status": "active"
      },
      {
        "id": "R34-001",
        "kind": "h2",
        "text": "Правильные части могут образовать неправильное целое",
        "source_ids": [
          "R34-001"
        ],
        "anchor": "R34-001",
        "content_status": "active"
      },
      {
        "id": "R34-002",
        "kind": "p",
        "text": "Пусть модель независимо выбирает комнату «кабинет» и группу «подсветка кухни». Каждое маргинальное решение имеет высокий confidence, но пара недопустима. Поэтому типизированный ответ проверяется целиком по текущему графу объектов и правам. Лучше выбирать из заранее построенных допустимых кортежей; варианты NONE и NEEDS_CLARIFICATION обязательны.",
        "source_ids": [
          "R34-002"
        ],
        "anchor": "R34-002",
        "content_status": "active"
      },
      {
        "id": "R34-003",
        "kind": "p",
        "text": "Калибровать нужно ошибку итогового действия, включая неверный объект, а не только каждую колонку ответов по отдельности. Перестановки вариантов, отрицания, одинаковые имена и неподходящая комната входят в независимый тест. Изменение графа после распознавания требует повторной проверки перед созданием команды.",
        "source_ids": [
          "R34-003"
        ],
        "anchor": "R34-003",
        "content_status": "active"
      },
      {
        "id": "R34-004",
        "kind": "p",
        "text": "Jev не является обязательной зависимостью E0/E1. Объявление 1.14 от 14 октября 2026 года уточняет возможности поставщика; ограничения опубликованного 1.13 не выдаются за установленные ошибки 1.14. Автономное размещение и применимость к нашему железу не подтверждены. При недоступности сервиса локальный типизированный интерпретатор и управление продолжают работать. [120]",
        "source_ids": [
          "R34-004"
        ],
        "anchor": "R34-004",
        "content_status": "active"
      }
    ],
    "origins": [
      "I11"
    ],
    "question": "Что именно полезного можно взять из Jev?",
    "intro": "Название Jev не означает JEPA. Здесь речь о модели и интерфейсе, которые ориентированы на ограниченные, типизированные решения. Вместо длинного свободного ответа можно спрашивать отдельно об объекте, действии и необходимости уточнения.\n\nДля Koobique важна эта форма договора. Сам внешний сервис не становится обязательной частью локальной станции. Мы проверим, что опубликовано о модели, где у неё границы и какие свойства нужно воспроизвести локальным решением.",
    "id": "ch-34",
    "slug": "chapter-34"
  },
  {
    "number": 35,
    "title": "JEPA и другие модели мира",
    "group": "AI",
    "units": [
      {
        "kind": "lead",
        "text": "Разные семейства отвечают на разные вопросы: понять фразу, сжать наблюдения, предсказать ряд, оценить последствия действия или выбрать политику.",
        "id": "I12-001",
        "old": "I12",
        "source_ids": [
          "I12-001"
        ],
        "anchor": "I12-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "JEPA: предсказывать представление",
        "id": "I12-002",
        "old": "I12",
        "source_ids": [
          "I12-002"
        ],
        "anchor": "I12-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В Joint-Embedding Predictive Architecture модель учится предсказывать внутреннее представление скрытой части данных по доступному контексту, а не обязательно восстанавливать каждый пиксель. V-JEPA 2 исследует понимание видео и действие-условное планирование в робототехнических задачах. Это не готовый контроллер комнатных реле. [121]",
        "id": "I12-003",
        "old": "I12",
        "source_ids": [
          "I12-003"
        ],
        "anchor": "I12-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "V-JEPA 2.1, опубликованная в марте 2026 года, развивает плотные пространственно-временные представления изображений и видео. Авторы описывают изменения функции обучения, самосупервизии и токенизации. [122] В официальном репозитории доступны код и модели; требования и лицензии необходимо проверять для выбранного checkpoint отдельно. [123]",
        "id": "I12-004",
        "old": "I12",
        "source_ids": [
          "I12-004"
        ],
        "anchor": "I12-004",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I12",
        "caption": "Идея JEPA в упрощённом виде. Предсказанное представление не является ни измерением, ни готовой командой. Для планирования нужны модель действия, целевая функция и отдельный исполнитель.",
        "id": "I12-005",
        "old": "I12",
        "source_ids": [
          "I12-005"
        ],
        "anchor": "I12-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первого Koobique камера не нужна. Загружать крупную видеомодель ради температуры и люксов — неподходящая исходная архитектура. Исследовательская альтернатива — небольшой энкодер временных окон наших сенсоров с масками пропусков и известными действиями. Это новая работа по обучению и проверке, а не автоматическая совместимость с весами V-JEPA.",
        "id": "I12-006",
        "old": "I12",
        "source_ids": [
          "I12-006"
        ],
        "anchor": "I12-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Dreamer, TD-MPC2 и PETS",
        "id": "I12-007",
        "old": "I12",
        "source_ids": [
          "I12-007"
        ],
        "anchor": "I12-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "DreamerV3 обучает модель среды и политику, используя предсказанные траектории. [124] TD-MPC2 сочетает латентную динамику и планирование для непрерывного управления. [125] PETS исследует вероятностные ансамбли динамики и планирование с учётом неопределённости. [126]",
        "id": "I12-008",
        "old": "I12",
        "source_ids": [
          "I12-008"
        ],
        "anchor": "I12-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для Koobique из этих направлений полезны действие-условная динамика, оценка расхождения моделей и разделение обучения/исполнения. Но их опубликованные результаты не измеряют безопасность нашего кабинета, работу с неизвестной нагрузкой или перенос из трёх домашних датчиков. Реальное обучение методом проб и ошибок на физических реле не становится приемлемым только потому, что политика успешно тренировалась в симуляторе.",
        "id": "I12-009",
        "old": "I12",
        "source_ids": [
          "I12-009"
        ],
        "anchor": "I12-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Модели временных рядов",
        "id": "I12-010",
        "old": "I12",
        "source_ids": [
          "I12-010"
        ],
        "anchor": "I12-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Chronos-2 — пример предобученного вероятностного прогнозирования с несколькими рядами и ковариатами. [127] Такой подход можно проверить для прогноза температуры или фонового света. Он не доказывает причинное влияние команды: хороший прогноз при старом поведении пользователя может ошибаться после нового режима управления.",
        "id": "I12-011",
        "old": "I12",
        "source_ids": [
          "I12-011"
        ],
        "anchor": "I12-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первый эталон здесь — последнее значение, суточный профиль при достаточной истории и простая физическая модель. Большая модель имеет смысл, если на отложенных эпизодах улучшает нужный показатель с приемлемыми памятью и задержкой.",
        "id": "I12-012",
        "old": "I12",
        "source_ids": [
          "I12-012"
        ],
        "anchor": "I12-012",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Активный вывод и ценность информации",
        "id": "I12-013",
        "old": "I12",
        "source_ids": [
          "I12-013"
        ],
        "anchor": "I12-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Active inference объединяет предпочтительные исходы и стремление получить информацию в одной вероятностной постановке. Исследования связывают её с байесовским выбором эксперимента и ожидаемой полезностью. [130] При этом вычислительная сложность и задание предпочтений — реальные инженерные вопросы, которым посвящены отдельные работы. [131]",
        "id": "I12-014",
        "old": "I12",
        "source_ids": [
          "I12-014"
        ],
        "anchor": "I12-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Мы заимствуем практический принцип: иногда лучше сначала измерить, чем действовать. Для первого кабинета достаточно явного расчёта ценности дополнительного наблюдения; не нужно объявлять весь runtime реализацией свободной энергии.",
        "id": "I12-015",
        "old": "I12",
        "source_ids": [
          "I12-015"
        ],
        "anchor": "I12-015",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Сравнение по роли в Koobique",
        "id": "I12-016",
        "old": "I12",
        "source_ids": [
          "I12-016"
        ],
        "anchor": "I12-016",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Подход",
            "Где проверять",
            "Почему не единственный мозг"
          ],
          [
            "Правила/автоматы",
            "Реле, ручные режимы, отказы",
            "Ограниченная адаптация и семантика"
          ],
          [
            "Малый классификатор",
            "Присутствие, намерение из фиксированных классов",
            "Нужны разметка и отказ при новом режиме"
          ],
          [
            "Физика + остаточная модель",
            "Свет, медленная температура",
            "Структура и область применимости задаются явно"
          ],
          [
            "JEPA-подобный энкодер",
            "Представления сложных потоков",
            "Нужны собственные данные и обучение"
          ],
          [
            "Dreamer / TD-MPC2",
            "Симуляционный исследовательский стенд",
            "Перенос и безопасное исследование не решены автоматически"
          ],
          [
            "Прогноз рядов",
            "Фон, тренды, предупреждения",
            "Прогноз не заменяет причинную модель"
          ],
          [
            "Языковая модель",
            "RU/EN, объяснение, предложение плана",
            "Нет полномочий на физический выход"
          ]
        ],
        "id": "I12-017",
        "old": "I12",
        "source_ids": [
          "I12-017"
        ],
        "anchor": "I12-017",
        "content_status": "active"
      },
      {
        "kind": "quote",
        "text": "Базовый AI-профиль: локальное понимание намерений, оценка присутствия и небольшая гибридная модель динамики. JEPA и обучение политики — отдельная исследовательская ветка с более высоким порогом доказательств.",
        "id": "I12-018",
        "old": "I12",
        "source_ids": [
          "I12-018"
        ],
        "anchor": "I12-018",
        "content_status": "active"
      },
      {
        "id": "R35-001",
        "kind": "h2",
        "text": "Чему должен научить опыт с JEPA",
        "source_ids": [
          "R35-001"
        ],
        "anchor": "R35-001",
        "content_status": "active"
      },
      {
        "id": "R35-002",
        "kind": "p",
        "text": "В JEPA-подобных семействах предсказание представлений требует защиты от вырожденного решения, когда всё кодируется одинаково. В изучаемых реализациях используются конкретные target/online-энкодеры, stop-gradient, усреднение параметров и другие ограничения; точную комбинацию берём из выбранной работы и кода, не считаем четыре буквы универсальным алгоритмом.",
        "source_ids": [
          "R35-002"
        ],
        "anchor": "R35-002",
        "content_status": "active"
      },
      {
        "id": "R35-003",
        "kind": "p",
        "text": "Для нескольких медленных комнатных каналов первым соперником служат ручные признаки, линейная модель и небольшой временной предиктор. Допуск исследования — улучшение на независимых сценах с учётом ресурсов, качества данных и ошибки физического решения. Linear probe, прогноз скрытого канала и перенос размещения полезны как разные проверки, но ни одна отдельно не доказывает безопасное управление. [121][122][123]",
        "source_ids": [
          "R35-003"
        ],
        "anchor": "R35-003",
        "content_status": "active"
      }
    ],
    "origins": [
      "I12"
    ],
    "question": "Предсказывать картинку, состояние и действие — одна задача?",
    "intro": "Представление видео помогает распознавать происходящее, прогноз временного ряда — оценивать будущую температуру, а модель с явным действием — сравнивать последствия управления. Политика выбора действия использует эти результаты на следующем уровне.\n\nКаждое семейство моделей рассматривается через задачу, необходимые данные и способ проверить пользу на датчиках Koobique. Для первого кабинета достаточно простых проверяемых моделей; крупная видеомодель остаётся отдельным исследованием.",
    "id": "ch-35",
    "slug": "chapter-35"
  },
  {
    "number": 36,
    "title": "Учиться на данных своего кабинета",
    "group": "Обучение",
    "units": [
      {
        "kind": "lead",
        "text": "История становится учебным материалом только тогда, когда известны условия сбора, действия системы и происхождение правильного ответа.",
        "id": "I13-001",
        "old": "I13",
        "source_ids": [
          "I13-001"
        ],
        "anchor": "I13-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Что сохранять рядом с измерением",
        "id": "I13-002",
        "old": "I13",
        "source_ids": [
          "I13-002"
        ],
        "anchor": "I13-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Помимо наблюдений нужны состояние исполнительных устройств, ручные действия, причины решений, версии моделей, параметры измерения и изменения размещения. Без этого рост света после ON можно перепутать с открытием шторы. Если система сама меняет частоту опроса, соответствующее решение тоже входит в журнал: отсутствие частых измерений может означать выбранную политику, а не спокойную среду.",
        "id": "I13-003",
        "old": "I13",
        "source_ids": [
          "I13-003"
        ],
        "anchor": "I13-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Разделяем сырые данные, производные признаки, метки человека и псевдометки модели. Последние полезны для экспериментов, но не становятся независимой истиной. Модель, обученная проверять саму себя по собственным ответам, может демонстрировать высокое согласие и повторять ту же ошибку.",
        "id": "I13-004",
        "old": "I13",
        "source_ids": [
          "I13-004"
        ],
        "anchor": "I13-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для происхождения данных полезен словарь PROV: сущность, действие преобразования, ответственный участник и связь производности. [104] В первой версии достаточно компактных идентификаторов и ссылок в локальном журнале; обязательная тяжёлая онтологическая инфраструктура не нужна.",
        "id": "I13-005",
        "old": "I13",
        "source_ids": [
          "I13-005"
        ],
        "anchor": "I13-005",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I13",
        "caption": "Путь модели к управлению. Обучение и теневое сравнение не имеют доступа к реле. Каждый переход требует отдельного результата проверки и версии артефакта.",
        "id": "I13-006",
        "old": "I13",
        "source_ids": [
          "I13-006"
        ],
        "anchor": "I13-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Разметка без непосильной работы",
        "id": "I13-007",
        "old": "I13",
        "source_ids": [
          "I13-007"
        ],
        "anchor": "I13-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для кабинета начинаем с эпизодов: вход, неподвижное чтение, уход, пустая комната с вентилятором, смена света, потеря датчика. Приложение позволяет отметить начало и конец и исправить метку позже. Пользователь не должен размечать каждую секунду вручную.",
        "id": "I13-008",
        "old": "I13",
        "source_ids": [
          "I13-008"
        ],
        "anchor": "I13-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Отдельно храним точность времени метки. Нажатие «я ушёл» может случиться через несколько секунд после выхода. Эту границу нельзя превращать в ложную точную истину при оценке задержки обнаружения.",
        "id": "I13-009",
        "old": "I13",
        "source_ids": [
          "I13-009"
        ],
        "anchor": "I13-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Разделение данных без утечки",
        "id": "I13-010",
        "old": "I13",
        "source_ids": [
          "I13-010"
        ],
        "anchor": "I13-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Случайное распределение соседних секунд по train и test создаёт иллюзию обобщения. Разделяем по целым эпизодам и дням; для проверки переноса — по расположению датчиков или комнате. После выбора порогов оставляем нетронутый итоговый набор. Иначе тест постепенно превращается в часть обучения.",
        "id": "I13-011",
        "old": "I13",
        "source_ids": [
          "I13-011"
        ],
        "anchor": "I13-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Входной снимок должен содержать только данные, доступные в момент решения. Сглаживание с будущими точками, окончательно исправленные метки или факт последующего ручного вмешательства способны незаметно дать модели информацию из будущего.",
        "id": "I13-012",
        "old": "I13",
        "source_ids": [
          "I13-012"
        ],
        "anchor": "I13-012",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Гибридная модель как первый кандидат",
        "id": "I13-013",
        "old": "I13",
        "source_ids": [
          "I13-013"
        ],
        "anchor": "I13-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для света задаём KQMATH032TOKEN: фон KQMATH033TOKEN, вклад лампы KQMATH034TOKEN, состояние KQMATH035TOKEN. Неизвестный KQMATH036TOKEN оцениваем разрешённым экспериментом. Нейросеть, если понадобится, предсказывает остаток или изменения фона, а не всю физику с нуля. Модель должна иметь возможность признать, что перестановка лампы разрушила прежнюю связь.",
        "id": "I13-014",
        "old": "I13",
        "source_ids": [
          "I13-014"
        ],
        "anchor": "I13-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для температуры задаём приближённую структуру накопления и теплообмена, а затем оцениваем коэффициенты. Если данных недостаточно для раздельного определения параметров, храним их неопределённость или используем более простое эффективное описание. Увеличение числа параметров не добавляет информации в датчики. Основы такой постановки рассматриваются в материалах по идентификации систем. [111]",
        "id": "I13-015",
        "old": "I13",
        "source_ids": [
          "I13-015"
        ],
        "anchor": "I13-015",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Обновление модели — смена поведения",
        "id": "I13-016",
        "old": "I13",
        "source_ids": [
          "I13-016"
        ],
        "anchor": "I13-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Новый файл весов получает версию, хеш, описание входов и выхода, набор тестов, область применения и совместимый исполнитель. Сначала модель работает на записях, затем в тени на текущих данных. В тени она может предлагать план, но результат не отправляется устройству.",
        "id": "I13-017",
        "old": "I13",
        "source_ids": [
          "I13-017"
        ],
        "anchor": "I13-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Переход к ограниченному управлению требует согласия и узкого набора действий. Плохая модель откатывается независимо от прошивки устройства. Базовый автомат и ручное управление остаются доступны. Автоматическое обучение в фоне допускается как вычисление кандидата, но не как скрытая выдача ему полномочий.",
        "id": "I13-018",
        "old": "I13",
        "source_ids": [
          "I13-018"
        ],
        "anchor": "I13-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Что replay не доказывает Можно повторно проверить логику на прежних наблюдениях. Нельзя достоверно узнать физический результат действия, которого тогда не было, просто подставив его в запись. Для контрфактической оценки нужна проверенная модель среды или безопасный новый эксперимент. Иначе «сравнение политик» использует вымышленный исход.",
        "id": "I13-019",
        "old": "I13",
        "source_ids": [
          "I13-019"
        ],
        "anchor": "I13-019",
        "content_status": "active"
      },
      {
        "id": "N-024",
        "kind": "h2",
        "text": "Что сравниваем перед допуском модели",
        "new": true,
        "source_ids": [
          "N-024"
        ],
        "anchor": "N-024",
        "content_status": "active"
      },
      {
        "id": "N-025",
        "kind": "p",
        "text": "Сначала фиксируем вопрос и сильный простой эталон. Для присутствия это может быть автомат с выдержкой и объединением доступных признаков. Для света — правило, которое уже учитывает собственную лампу. Для температуры — последнее значение или простая тепловая модель. Модель не считается полезной только потому, что выиграла у заведомо плохо настроенного порога.",
        "new": true,
        "source_ids": [
          "N-025"
        ],
        "anchor": "N-025",
        "content_status": "active"
      },
      {
        "id": "N-026",
        "kind": "p",
        "text": "Отчёт сравнения хранит не одну оценку accuracy, а последствия: время ненужного света, время недостаточного освещения, число переключений, пропущенные ручные приоритеты, задержку и расход памяти. Эпизоды с новой расстановкой или другим фоном выделяются отдельно. Когда новая модель не даёт полезного выигрыша, правильное решение — оставить эталон.",
        "new": true,
        "source_ids": [
          "N-026"
        ],
        "anchor": "N-026",
        "content_status": "active"
      },
      {
        "id": "R36-001",
        "kind": "h2",
        "text": "Воспроизвести решение и воспроизвести нейросеть — разные задачи",
        "source_ids": [
          "R36-001"
        ],
        "anchor": "R36-001",
        "content_status": "active"
      },
      {
        "id": "R36-002",
        "kind": "p",
        "text": "Аудиторское воспроизведение использует сохранённый decision_snapshot: только сведения, доступные исходному решению, версии конфигурации и политик, хеши моделей, seed и события таймеров. При полном повторе потока сохраняют порядок поступления и допуска данных, привязанный к загрузке и виртуальным часам; время физического события хранится отдельно. Сортировка по времени события, источнику и последовательности подходит для ретроспективного пересчёта истории, но не переносит позднее наблюдение в знания прошлого решения. Текущие часы и новый descriptor не подменяют исторические. Для целочисленного автомата E0 проверяется совпадение нормализованного журнала решений.",
        "source_ids": [
          "R36-002"
        ],
        "anchor": "R36-002",
        "content_status": "active"
      },
      {
        "id": "R36-003",
        "kind": "p",
        "text": "Побайтное равенство нейросетевого вывода на ARM и x86 не обещается без специального детерминированного backend. Есть отдельные уровни проверки: сохранённый вывод модели воспроизводит аудит; повторный inference проходит заданные допуски; физический результат требует экспериментальной среды. Несовпадение чисел в допустимых пределах не должно менять разрешение опасного действия на границе без дополнительного анализа.",
        "source_ids": [
          "R36-003"
        ],
        "anchor": "R36-003",
        "content_status": "active"
      },
      {
        "id": "R36-004",
        "kind": "p",
        "text": "Данные делятся по времени и эпизодам: обучение на прошлом, настройка на следующем участке, тест на ещё более позднем. Между окнами оставляют интервал, закрывающий перекрытие входов и целевого горизонта. Нельзя случайно распределять соседние секунды одной истории между train и test. Если планируется off-policy оценивание, необходимы известная политика выбора и вероятности действий; их нельзя восстановить задним числом из одного результата.",
        "source_ids": [
          "R36-004"
        ],
        "anchor": "R36-004",
        "content_status": "active"
      },
      {
        "id": "R3-36-EVAL-H",
        "kind": "h2",
        "text": "Разделить наблюдаемый результат и альтернативную политику",
        "source_ids": [],
        "anchor": "R3-36-EVAL-H",
        "content_status": "r3_addition"
      },
      {
        "id": "R3-36-EVAL-P1",
        "kind": "p",
        "text": "В журнале действующей политики известны выполненные команды и полученные измерения. Теневая политика получает те же доступные входы и может сравнить свои предложения с эталоном, но её невыполненные действия не имеют наблюдённого физического результата. Поэтому затраты энергии и освещённость для альтернативы отмечаются как модельная оценка с версией симулятора, а не как факт из комнаты.",
        "source_ids": [],
        "anchor": "R3-36-EVAL-P1",
        "content_status": "r3_addition"
      },
      {
        "id": "R3-36-EVAL-P2",
        "kind": "p",
        "text": "Сравнение света начинается с трёх явно заданных вариантов: простого порога как диагностического примера, сильного автомата, который уже учитывает собственный вклад лампы, и нового кандидата. В каждом прогоне одинаковы внешние условия, ограничения переключений и ручные воздействия. Результаты разделяют по времени недостаточного света, числу переключений, времени ON, ложным действиям и отказам от решения; один выигранный показатель не скрывает ухудшение остальных.",
        "source_ids": [],
        "anchor": "R3-36-EVAL-P2",
        "content_status": "r3_addition"
      },
      {
        "id": "R3-36-EVAL-P3",
        "kind": "p",
        "text": "После воспроизведения учебного рассвета добавляют эпизоды переменной облачности, ошибочного коэффициента, переноса датчика, фонового источника, пропуска измерения и ручного OFF. Начальное состояние, seed и параметры сохраняют, а проверочные эпизоды отделяют от настройки. Новая модель получает физический допуск только после сопоставления симуляции с разрешённым опытом в комнате. Отсутствие выгоды относительно сильного автомата — достаточное основание оставить автомат.",
        "source_ids": [],
        "anchor": "R3-36-EVAL-P3",
        "content_status": "r3_addition"
      }
    ],
    "origins": [
      "I13"
    ],
    "question": "Как собрать данные, которые действительно проверяют модель?",
    "intro": "Тысячи соседних отсчётов одной спокойной комнаты не равны тысячам независимых ситуаций. Если случайно перемешать их между обучением и тестом, оценка может оказаться слишком красивой.\n\nНам нужны целые эпизоды: вход, неподвижная работа, уход, перестановка сенсора, изменение дневного света, потеря связи. Их условия и границы сохраняются. Новая модель сначала сравнивается с сильным простым эталоном на отложенных эпизодах, а не сразу получает доступ к реле.",
    "id": "ch-36",
    "slug": "chapter-36"
  },
  {
    "number": 37,
    "title": "Иногда лучше сначала измерить",
    "group": "Исследование",
    "units": [
      {
        "kind": "lead",
        "text": "Система выбирает следующее измерение по его ожидаемой пользе. Так можно получить сведения для решения, сохраняя ограниченную частоту опроса остальных сенсоров.",
        "id": "I14-001",
        "old": "I14",
        "source_ids": [
          "I14-001"
        ],
        "anchor": "I14-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Измерение как действие",
        "id": "I14-002",
        "old": "I14",
        "source_ids": [
          "I14-002"
        ],
        "anchor": "I14-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Дополнительное измерение имеет цену: энергию, время, трафик, приватность и иногда физическое вмешательство. Оно полезно, если способно изменить выбор действия или обнаружить неисправность. Если результат не повлияет ни на одно допустимое решение, измерять чаще только ради большого архива необязательно.",
        "id": "I14-003",
        "old": "I14",
        "source_ids": [
          "I14-003"
        ],
        "anchor": "I14-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Простой расчёт: KQMATH037TOKEN — ожидаемое уменьшение потери после нового наблюдения; KQMATH038TOKEN — стоимость его получения. Измерение рассматривается, если KQMATH039TOKEN и оно разрешено. Это проектная упрощённая постановка, а не полная реализация active inference. Связь выбора информации и действия имеет отдельную исследовательскую основу. [130]",
        "id": "I14-004",
        "old": "I14",
        "source_ids": [
          "I14-004"
        ],
        "anchor": "I14-004",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I14",
        "caption": "Выбор следующего наблюдения. Вариант «измерить» проходит те же проверки ресурса и приватности, что и другие действия. Базовое покрытие не выключается полностью.",
        "id": "I14-005",
        "old": "I14",
        "source_ids": [
          "I14-005"
        ],
        "anchor": "I14-005",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Три безопасных примера",
        "id": "I14-006",
        "old": "I14",
        "source_ids": [
          "I14-006"
        ],
        "anchor": "I14-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сомнение в присутствии. PIR давно молчит, радар даёт пограничный сигнал. Можно временно получить более подробное окно радара, если модуль поддерживает такой режим и он проверен, либо спросить пользователя. Нельзя молча включить микрофон или камеру, на которые не выдавалось разрешение.",
        "id": "I14-007",
        "old": "I14",
        "source_ids": [
          "I14-007"
        ],
        "anchor": "I14-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Проверка света. После команды ON запрашиваем новое измерение с известным временем интеграции, чтобы проверить изменение. Опрос до завершения преобразования не ускоряет физический результат, а лишь возвращает старые данные.",
        "id": "I14-008",
        "old": "I14",
        "source_ids": [
          "I14-008"
        ],
        "anchor": "I14-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Подозрение на дрейф. Два температурных канала начинают расходиться. Вместо немедленной поправки одного по другому проводим согласованный сравнительный эпизод и проверяем размещение. Оба датчика могут одновременно ошибаться из-за общей причины.",
        "id": "I14-009",
        "old": "I14",
        "source_ids": [
          "I14-009"
        ],
        "anchor": "I14-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Самокалибровка — только в разрешённом эксперименте",
        "id": "I14-010",
        "old": "I14",
        "source_ids": [
          "I14-010"
        ],
        "anchor": "I14-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для низковольтной лампы можно предложить короткую последовательность OFF/ON с ограниченным числом переключений. Пользователь подтверждает, что это допустимо сейчас. Эксперимент фиксирует фон, выдерживает паузу, наблюдает эффект и прекращается при изменении условий или ручном вмешательстве.",
        "id": "I14-011",
        "old": "I14",
        "source_ids": [
          "I14-011"
        ],
        "anchor": "I14-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Нужно учитывать фон: облако может снизить освещённость в середине испытания. Несколько чередующихся наблюдений и контроль стабильности дают больше оснований, чем одно вычитание двух чисел. Но даже такая процедура не доказывает вклад при всех будущих положениях шторы.",
        "id": "I14-012",
        "old": "I14",
        "source_ids": [
          "I14-012"
        ],
        "anchor": "I14-012",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Предел обучения «из любопытства»",
        "id": "I14-013",
        "old": "I14",
        "source_ids": [
          "I14-013"
        ],
        "anchor": "I14-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Система не должна включать неизвестную розетку, чтобы узнать, что к ней подключено. Не проводим автоматические эксперименты с нагревом, замками, движущейся механикой, медицинскими функциями и другой существенной опасностью. Для каждого процесса существует отдельный белый список исследовательских действий и условий.",
        "id": "I14-014",
        "old": "I14",
        "source_ids": [
          "I14-014"
        ],
        "anchor": "I14-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Активный сбор создаёт смещение: интересные модели случаи начинают записываться чаще остальных. Поэтому сохраняем базовый режим измерения, журнал выбора частоты и независимую выборку эпизодов. Иначе датасет постепенно перестаёт описывать обычную комнату.",
        "id": "I14-015",
        "old": "I14",
        "source_ids": [
          "I14-015"
        ],
        "anchor": "I14-015",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Аппаратные последствия",
        "id": "I14-016",
        "old": "I14",
        "source_ids": [
          "I14-016"
        ],
        "anchor": "I14-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Управляемая частота требует понятных команд профиля сенсора, а не произвольного доступа AI к его регистрам. Прошивка проверяет диапазоны и подтверждает фактический режим. При смене конфигурации новая серия наблюдений содержит её ревизию; обработчик не объединяет окна с разными характеристиками как однородные.",
        "id": "I14-017",
        "old": "I14",
        "source_ids": [
          "I14-017"
        ],
        "anchor": "I14-017",
        "content_status": "active"
      },
      {
        "id": "R37-001",
        "kind": "h2",
        "text": "Ценность нового измерения — ожидаемое улучшение решения",
        "source_ids": [
          "R37-001"
        ],
        "anchor": "R37-001",
        "content_status": "active"
      },
      {
        "id": "R37-002",
        "kind": "p",
        "text": "EVSI сравнивает ожидаемую полезность лучшего разрешённого действия после дополнительного наблюдения с лучшим действием при текущих данных. Это ожидание по возможным результатам измерения; оно зависит от модели и может быть ошибочным. Из разницы вычитаются сопоставимые затраты, например энергия и задержка, если владелец согласовал их шкалу.",
        "source_ids": [
          "R37-002"
        ],
        "anchor": "R37-002",
        "content_status": "active"
      },
      {
        "id": "R37-003",
        "kind": "p",
        "text": "Приватность, согласие человека, допустимый диапазон мощности и права на микрофон сначала задают множество разрешённых измерений. Они не переводятся в маленький штраф, который может перевесить любопытство модели. Для E0 активное измерение означает запросить ещё один отсчёт допустимого канала; экспериментальное включение лампы требует отдельного разрешённого окна и возможности прервать его.",
        "source_ids": [
          "R37-003"
        ],
        "anchor": "R37-003",
        "content_status": "active"
      },
      {
        "kind": "quote",
        "text": "Первый исследовательский опыт Koobique оценивает влияние разрешённой низковольтной лампы и пользу дополнительного измерения. Его результатом становятся проверенные параметры и границы применимости; более широкий автономный эксперимент требует отдельного допуска.",
        "id": "I14-018",
        "old": "I14",
        "source_ids": [
          "I14-018"
        ],
        "anchor": "I14-018",
        "content_status": "active"
      }
    ],
    "origins": [
      "I14"
    ],
    "question": "Может ли система сама выбрать следующее наблюдение?",
    "intro": "Система может попросить ещё один отсчёт, временно повысить частоту измерения или предложить владельцу опыт с низковольтной лампой. Полезность такой самостоятельности определяется тем, улучшает ли полученная информация конкретную оценку или решение.\n\nУ эксперимента есть разрешение, длительность, пределы воздействия и условие остановки. Включать неизвестную нагрузку ради новых данных запрещено. Дополнительное действие оправдано только в разрешённой области и с понятной исследовательской целью.",
    "id": "ch-37",
    "slug": "chapter-37"
  },
  {
    "number": 38,
    "title": "Последняя проверка перед действием",
    "group": "Исполнение",
    "units": [
      {
        "kind": "lead",
        "text": "Модель формирует предложение, которое проходит независимую проверку. Устройство применяет собственные ограничения, а результат действия сопоставляется с физическими наблюдениями.",
        "id": "I15-001",
        "old": "I15",
        "source_ids": [
          "I15-001"
        ],
        "anchor": "I15-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Три уровня допуска",
        "id": "I15-002",
        "old": "I15",
        "source_ids": [
          "I15-002"
        ],
        "anchor": "I15-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Права отвечают, кто может воздействовать на объект и в каком диапазоне. Политика процесса проверяет свежесть, режим, ручной приоритет и допустимость плана. Локальная защита устройства удерживает выход в предусмотренных границах даже без связи. Это разные механизмы; успешная аутентификация не заменяет ни один из двух последних.",
        "id": "I15-003",
        "old": "I15",
        "source_ids": [
          "I15-003"
        ],
        "anchor": "I15-003",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I15",
        "caption": "Путь предложения к выходу. Контроль повторяется перед отправкой и на устройстве. Отказ возвращает объяснимую причину, а не просьбу к модели «убедить» защитный блок.",
        "id": "I15-004",
        "old": "I15",
        "source_ids": [
          "I15-004"
        ],
        "anchor": "I15-004",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Контракт предложения",
        "id": "I15-005",
        "old": "I15",
        "source_ids": [
          "I15-005"
        ],
        "anchor": "I15-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Предлагаем объект ActionProposal: уникальный ID, объект и действие, аргументы, инициатор, версия прав, снимок состояния, срок действия, предусловия и ожидаемый эффект. Он не содержит секретов устройства и не может указать произвольный сетевой адрес вместо зарегистрированного ресурса.",
        "id": "I15-006",
        "old": "I15",
        "source_ids": [
          "I15-006"
        ],
        "anchor": "I15-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Валидатор проверяет типы и единицы; арбитр — конфликт владельцев и ручного режима; исполнитель — актуальность условий и возможность доставки. Проверки с числами выполняются точно в коде. Валидация схемы — первый барьер, не полная безопасность.",
        "id": "I15-007",
        "old": "I15",
        "source_ids": [
          "I15-007"
        ],
        "anchor": "I15-007",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Гонка между планом и кнопкой",
        "id": "I15-008",
        "old": "I15",
        "source_ids": [
          "I15-008"
        ],
        "anchor": "I15-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В 10:00:00 модель предлагает ON. В 10:00:01 человек нажимает OFF. В 10:00:02 план готов к отправке. Если проверить ручной режим только до вычисления, старый план отменит волю пользователя.",
        "id": "I15-009",
        "old": "I15",
        "source_ids": [
          "I15-009"
        ],
        "anchor": "I15-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Разводим три независимых величины: controller_fence связывает назначение станции с её ключом; output_revision меняется при установке выхода; manual_revision меняется при ручном намерении, даже если электрический уровень не изменился. Все локальные версии дополнены boot_id. Ручной режим — отдельное состояние, а не только число. Сверка новой версии после связи не даёт автоматизации права отменить действующий ручной запрет.",
        "id": "I15-010",
        "old": "I15",
        "source_ids": [
          "I15-010"
        ],
        "anchor": "I15-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Доставка, повтор и неизвестный исход",
        "id": "I15-011",
        "old": "I15",
        "source_ids": [
          "I15-011"
        ],
        "anchor": "I15-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Используем семантику KQIP из глав 21–22: ID команды, дедупликация, срок действия, подтверждения и отдельный результат. Команда «установить ON» легче переносит повтор, чем «переключить». Но идемпотентность значения не делает любой физический процесс безрисковым: повторная подача питания может иметь особенности конкретной нагрузки.",
        "id": "I15-012",
        "old": "I15",
        "edited": true,
        "source_ids": [
          "I15-012"
        ],
        "anchor": "I15-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Если подтверждение потеряно, исход может быть неизвестным. Сначала читаем состояние и проверяем наблюдения. Для неидемпотентной операции автоматический повтор запрещается или требует специальной процедуры. Нельзя объявить её невыполненной только из-за тайм-аута связи.",
        "id": "I15-013",
        "old": "I15",
        "source_ids": [
          "I15-013"
        ],
        "anchor": "I15-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Пять записей о намерении и результате",
        "id": "I15-014",
        "old": "I15",
        "source_ids": [
          "I15-014"
        ],
        "anchor": "I15-014",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Запись",
            "Что она утверждает"
          ],
          [
            "CommandIntent / ACCEPTED",
            "Core транзакционно сохранил намерение и outbox"
          ],
          [
            "DeliveryAttempt / DISPATCHED",
            "Началась попытка отправки; RECEIVED требует ответа MCU"
          ],
          [
            "ExecutionResult / APPLIED",
            "MCU сообщает результат с boot_id и ревизиями"
          ],
          [
            "OutputSnapshot",
            "Последний известный выход по версии, независимо от ACK"
          ],
          [
            "EffectEvidence / GoalEvaluation",
            "Наблюдение эффекта и оценка цели в названном интервале"
          ]
        ],
        "id": "I15-015",
        "old": "I15",
        "source_ids": [
          "I15-015"
        ],
        "anchor": "I15-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Ток лампы помогает обнаружить обрыв, но не заменяет световой контроль. Рост света может быть вызван окном; при неоднозначности результат «эффект совместим с ожиданием» честнее абсолютного утверждения причинности.",
        "id": "I15-016",
        "old": "I15",
        "source_ids": [
          "I15-016"
        ],
        "anchor": "I15-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Независимость защиты",
        "id": "I15-017",
        "old": "I15",
        "source_ids": [
          "I15-017"
        ],
        "anchor": "I15-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Проверка ограничений не спрашивает ту же модель, правильно ли она решила. В E0 это небольшой Rust-модуль Core; его независимость логическая, не аппаратная: panic или OOM могут остановить весь процесс. Последний локальный рубеж — прошивка с собственным владельцем выхода; физическое снятие питания остаётся отдельным средством. Для опасных нагрузок потребуются независимые схемы и профиль риска, отсутствующие у учебной лампы. Модуль проверки остаётся малым и тестируется отдельно от предсказателя; для решения ему не требуется завершение GPU-задачи или AI-worker. Это требование к реализации не устраняет общие отказы процесса: проверяется поведение при зависании worker, исчерпании ресурсов и остановке Core.",
        "id": "I15-018",
        "old": "I15",
        "source_ids": [
          "I15-018"
        ],
        "anchor": "I15-018",
        "content_status": "active"
      },
      {
        "kind": "quote",
        "text": "Последствия ошибочного предложения ограничиваются проверяемыми условиями исполнения. Для каждого действия указаны ресурс, владелец, срок, пределы и способ наблюдения результата.",
        "id": "I15-019",
        "old": "I15",
        "source_ids": [
          "I15-019"
        ],
        "anchor": "I15-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Ни распознавание речи, ни модель мира не получают прямой доступ к GPIO, SSH или ключам. Между предложением и выходом стоит независимый исполнитель с повторной проверкой условий. После команды отдельно фиксируются подтверждение выхода и достижение физической цели.",
        "id": "I27-006",
        "old": "I27",
        "edited": true,
        "source_ids": [
          "I27-006"
        ],
        "anchor": "I27-006",
        "content_status": "active"
      },
      {
        "id": "N-027",
        "kind": "h2",
        "text": "Разные счётчики защищают от разных устареваний",
        "new": true,
        "source_ids": [
          "N-027"
        ],
        "anchor": "N-027",
        "content_status": "active"
      },
      {
        "id": "N-028",
        "kind": "p",
        "text": "ID команды отвечает на вопрос «это тот же запрос или новый?». Версия выхода — «менялось ли состояние после снимка?». Controller fence — «это ещё действующий хозяин инсталляции?». Локальная ревизия ручного действия — «не нажал ли человек кнопку после плана?». Эти поля связаны, но один общий счётчик не обязан надёжно заменять все четыре.",
        "new": true,
        "source_ids": [
          "N-028"
        ],
        "anchor": "N-028",
        "content_status": "active"
      },
      {
        "id": "N-029",
        "kind": "p",
        "text": "Конкретное двоичное представление следует закрепить в спецификации. На устройстве проверяется та часть предусловий, которая доступна локально; на станции — права, контекст и план. Во время разрыва связи кнопка меняет локальную ревизию. После соединения сначала согласуется текущее состояние, а не отправляется накопленная очередь старых желаний.",
        "new": true,
        "source_ids": [
          "N-029"
        ],
        "anchor": "N-029",
        "content_status": "active"
      },
      {
        "id": "R38-001",
        "kind": "h2",
        "text": "Приоритеты и атомарная граница действия",
        "source_ids": [
          "R38-001"
        ],
        "anchor": "R38-001",
        "content_status": "active"
      },
      {
        "id": "R38-002",
        "kind": "table",
        "rows": [
          [
            "Приоритет",
            "Что означает"
          ],
          [
            "1. Аппаратная защита / физическое снятие питания",
            "Не блокируется manual hold и AI"
          ],
          [
            "2. Локальные ограничения MCU",
            "Недопустимый режим, новая boot, срок и межблокировки"
          ],
          [
            "3. Подтверждённое ручное намерение",
            "Обычная автоматика не отменяет его"
          ],
          [
            "4. Разрешённая автоматизация",
            "Только свежие данные, допущенный профиль, текущие версии"
          ],
          [
            "5. Исследовательский кандидат",
            "По умолчанию только предложение"
          ]
        ],
        "source_ids": [
          "R38-002"
        ],
        "anchor": "R38-002",
        "content_status": "active"
      },
      {
        "id": "R38-003",
        "kind": "p",
        "text": "Guard внутри Core уменьшает доверие к модели, но разделяет с Core память и отказ процесса. Поэтому инструкции узла не сводятся к «сервер уже проверил». MCU проверяет собственные boot, revision, срок и режим непосредственно в владельце выхода. Граница доверия остаётся: авторизованный, но скомпрометированный Core может лгать о пользовательской причине. E0 не заявляет защиту от всех действий такого контроллера; для более опасного применения нужны отдельные локальные полномочия и независимое оборудование.",
        "source_ids": [
          "R38-003"
        ],
        "anchor": "R38-003",
        "content_status": "active"
      },
      {
        "id": "R38-004",
        "kind": "p",
        "text": "Внешний настенный выключатель либо снятие питания лампы фиксируются как возможное вмешательство. APPLIED GPIO при отсутствии света не становится ложным успехом. Система не должна бесконечно щёлкать реле, пытаясь «исправить» физически разорванную цепь: лимит повторных попыток и уведомление входят в профиль.",
        "source_ids": [
          "R38-004"
        ],
        "anchor": "R38-004",
        "content_status": "active"
      },
      {
        "id": "R3-38-GATE-H",
        "kind": "h2",
        "text": "Модель не выдаёт себе допуск",
        "source_ids": [],
        "anchor": "R3-38-GATE-H",
        "content_status": "r3_addition"
      },
      {
        "id": "R3-38-GATE-P1",
        "kind": "p",
        "text": "Исполнитель получает не только предлагаемое действие, но и ссылку на исходный снимок, версию модели и область, для которой проверено её поведение. Core выводит права из своей текущей записи владельца и пакета процесса; поля ответа worker не создают эти права. Затем проверяются свежесть, состояние ручного режима, действительность привязки модели к размещению и допустимость воздействия. На MCU повторяются boot, fence, срок, ревизии и локальные ограничения.",
        "source_ids": [],
        "anchor": "R3-38-GATE-P1",
        "content_status": "r3_addition"
      },
      {
        "id": "R3-38-GATE-P2",
        "kind": "p",
        "text": "У допуска есть область: конкретный объект, набор действий, диапазон, версия и условия измерения. Замена весов, калибровки, нормализации или размещения может вывести исполнение за эту область. До повторной проверки сохраняется согласованный базовый режим либо остановка автоматических действий по политике нагрузки. Ручная кнопка и независимая аппаратная защита не ждут, пока модель объяснит результат.",
        "source_ids": [],
        "anchor": "R3-38-GATE-P2",
        "content_status": "r3_addition"
      },
      {
        "id": "R3-38-GATE-P3",
        "kind": "p",
        "text": "Отдельная приёмочная история связывает все уровни: worker получает снимок, человек нажимает OFF, затем приходит позднее предложение. Сохраняется исходная привязка запроса; обновление snapshot не превращает старый ответ в новое намерение. Исполнитель отказывает по изменённым условиям, фиксирует причину и сохраняет текущий выход. Это уже предусмотренный архитектурой принцип, который нужно подтвердить реализацией каждого клиентского пути, включая голос.",
        "source_ids": [],
        "anchor": "R3-38-GATE-P3",
        "content_status": "r3_addition"
      }
    ],
    "origins": [
      "I15"
    ],
    "question": "Что проверяется за мгновение до выхода на устройство?",
    "intro": "План мог быть правильным секунду назад. Затем человек нажал кнопку, датчик устарел или управление перешло другой станции. Поэтому разрешение проверяется не только при составлении плана, но и перед исполнением.\n\nЭтот последний рубеж не является ещё одним голосованием AI. Он проверяет формальные условия: владельца ресурса, эпоху, срок, версию состояния и пределы команды. А после отправки следит, какой уровень результата действительно получен.",
    "id": "ch-38",
    "slug": "chapter-38"
  },
  {
    "number": 39,
    "title": "Память системы и воспроизведение событий",
    "group": "Хранение",
    "units": [
      {
        "kind": "lead",
        "text": "Быстрая запись полезна только тогда, когда понятно, что система может потерять при сбое.",
        "y": 168.675537109375,
        "page": 85,
        "old": 25,
        "id": "A25-001",
        "source_ids": [
          "A25-001"
        ],
        "anchor": "A25-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В первом Koobique хранятся четыре разных класса данных: конфигурация и ключевые привязки; журнал принятых команд; текущие наблюдения; длительная история. У них разные требования к сохранности. Не нужно делать каждое измерение температуры таким же дорогим, как смену владельца, но нельзя терять принятую команду и продолжать показывать пользователю успех.",
        "y": 219.78143310546875,
        "page": 85,
        "old": 25,
        "id": "A25-002",
        "source_ids": [
          "A25-002"
        ],
        "anchor": "A25-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "SQLite как исходный выбор",
        "y": 315.55059814453125,
        "page": 85,
        "old": 25,
        "id": "A25-003",
        "source_ids": [
          "A25-003"
        ],
        "anchor": "A25-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "SQLite подходит для одной станции, если архитектура учитывает одного писателя и локальный накопитель. WAL позволяет чтению и записи сосуществовать, но требует управления checkpoint и длительными читателями. Активная база не размещается на SMB/NFS только ради удобной резервной копии. В 2026 году исправлена ошибка WAL-reset; сборка должна содержать исправленную версию или подтверждённый backport. [51]",
        "y": 342.5682373046875,
        "page": 85,
        "old": 25,
        "id": "A25-004",
        "source_ids": [
          "A25-004"
        ],
        "anchor": "A25-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Это сознательный выбор зрелого SQL-хранилища, а не отказ от Rust. redb остаётся альтернативой для транзакционного key-value слоя, если его модель лучше подходит данным и прошла такие же проверки. Сравнение включает запросы, резервирование, миграции, память и поведение при отключении питания, а не только язык реализации. [53]",
        "y": 446.92218017578125,
        "page": 85,
        "old": 25,
        "id": "A25-005",
        "source_ids": [
          "A25-005"
        ],
        "anchor": "A25-005",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Транзакция принятия и outbox",
        "y": 56.314456939697266,
        "page": 86,
        "old": 25,
        "id": "A25-006",
        "source_ids": [
          "A25-006"
        ],
        "anchor": "A25-006",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A32",
        "caption": "Команда и запись на отправку сохраняются в одной транзакции. Dispatcher фиксирует начало попытки доставки до записи в сокет, затем доставляет команду и сверяет результат. Сбой после начала попытки оставляет неизвестный исход до свидетельства устройства; он не доказывает, что действие не выполнялось.",
        "y": 91.10079193115234,
        "page": 86,
        "old": 25,
        "id": "A25-007",
        "source_ids": [
          "A25-007"
        ],
        "anchor": "A25-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В одной транзакции сохраняются command_id, источник, неизменяемые параметры, условия исполнения, минимальные основания и outbox. Только после успешного commit выдаётся ACCEPTED. В живой загрузке Core допускает повтор того же тела и ID до исходного срока. Перед первой записью пакета в сокет долговечно фиксируется DeliveryAttempt. После reboot/resume отсутствие такой отметки позволяет назвать запрос неотправленным/EXPIRED; отметка без результата означает возможно исполненную команду и требует UNKNOWN/сверки. Старый outbox не становится новым намерением. Приёмка включает сбои до и после commit отметки, до и после write и между GPIO и ACK.",
        "y": 461.3018798828125,
        "page": 86,
        "old": 25,
        "id": "A25-008",
        "source_ids": [
          "A25-008"
        ],
        "anchor": "A25-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Если сеть получила пакет, но станция упала до записи результата, при восстановлении проводится сверка. Физический эффект нельзя откатить транзакцией SQLite. Поэтому важны идемпотентность, история результатов на устройстве и состояние UNKNOWN.",
        "y": 533.705810546875,
        "page": 86,
        "old": 25,
        "id": "A25-009",
        "source_ids": [
          "A25-009"
        ],
        "anchor": "A25-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Режим синхронизации",
        "y": 597.5249633789062,
        "page": 86,
        "old": 25,
        "id": "A25-010",
        "source_ids": [
          "A25-010"
        ],
        "anchor": "A25-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для критичного журнала требуется режим, соответствующий обещанной сохранности при потере питания. В WAL выбор FULL/NORMAL меняет момент синхронизации и допустимую потерю последних транзакций. Эти настройки задаются осознанно для конкретного соединения/хранилища и проверяются; нельзя просто скопировать «быстрые PRAGMA» из случайного примера. [93]",
        "y": 624.5426025390625,
        "page": 86,
        "old": 25,
        "id": "A25-011",
        "source_ids": [
          "A25-011"
        ],
        "anchor": "A25-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Телеметрию можно записывать пакетами. Если принято решение допускать потерю нескольких последних секунд истории, оно отображается в требованиях. Критический журнал и история могут жить в разных базах, но тогда нет общей атомарности транзакций между ними: это учитывается в проекте.",
        "y": 712.9215698242188,
        "page": 86,
        "old": 25,
        "id": "A25-012",
        "source_ids": [
          "A25-012"
        ],
        "anchor": "A25-012",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Объём данных",
        "y": 56.314456939697266,
        "page": 87,
        "old": 25,
        "id": "A25-013",
        "source_ids": [
          "A25-013"
        ],
        "anchor": "A25-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Условные 100 событий в секунду по 200 байт дают 1,728 ГБ в сутки до индексов и служебных расходов. Для кабинета такая частота обычно избыточна, но расчёт показывает опасность бесконтрольного логирования. 10 событий в секунду уменьшают исходный объём в десять раз, однако политика хранения всё равно нужна.",
        "y": 83.33208465576172,
        "page": 87,
        "old": 25,
        "id": "A25-014",
        "source_ids": [
          "A25-014"
        ],
        "anchor": "A25-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Raw-история сохраняется ограниченный срок, затем строятся агрегаты по минутам/часам. Агрегат содержит минимум, максимум, среднее, число валидных образцов и долю пропусков. Только среднее скрывает короткие, но важные изменения.",
        "y": 155.73602294921875,
        "page": 87,
        "old": 25,
        "id": "A25-015",
        "source_ids": [
          "A25-015"
        ],
        "anchor": "A25-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Аудио хранится отдельно и по умолчанию не архивируется постоянно. Модели, резервные копии и диагностические пакеты имеют собственные каталоги и квоты. Заполненный диск должен сначала отключить необязательный сбор данных, а не сломать конфигурацию и обновление.",
        "y": 212.16497802734375,
        "page": 87,
        "old": 25,
        "id": "A25-016",
        "source_ids": [
          "A25-016"
        ],
        "anchor": "A25-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Резервная копия",
        "y": 291.9591369628906,
        "page": 87,
        "old": 25,
        "id": "A25-017",
        "source_ids": [
          "A25-017"
        ],
        "anchor": "A25-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для работающей SQLite применяется согласованный Backup API или эквивалентная процедура, а не копирование одного файла без WAL. Копия включает описание версии, конфигурацию, ключи в защищённом виде и необходимые данные адаптеров. Проверка чтения архива не равна проверке восстановления всей инсталляции. [52]",
        "y": 318.9767761230469,
        "page": 87,
        "old": 25,
        "id": "A25-018",
        "source_ids": [
          "A25-018"
        ],
        "anchor": "A25-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Ключи восстановления должны быть доступны владельцу отдельно от единственного хаба. Но резервная копия не предназначена для одновременного запуска двух управляющих станций. После переноса меняется controller_fence, а старая копия не получает автоматически действующие полномочия.",
        "y": 391.3807067871094,
        "page": 87,
        "old": 25,
        "id": "A25-019",
        "source_ids": [
          "A25-019"
        ],
        "anchor": "A25-019",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Миграции",
        "y": 471.17486572265625,
        "page": 87,
        "old": 25,
        "id": "A25-020",
        "source_ids": [
          "A25-020"
        ],
        "anchor": "A25-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Новая версия сначала добавляет совместимые поля, затем переводит данные и только позднее удаляет старые структуры. Необратимая миграция должна быть явно согласована с механизмом отката ОС. Смена формата всей истории ради одного поля чаще всего не оправдана.",
        "y": 498.1925354003906,
        "page": 87,
        "old": 25,
        "id": "A25-021",
        "source_ids": [
          "A25-021"
        ],
        "anchor": "A25-021",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Испытание, без которого БД не принята",
        "y": 586.8663330078125,
        "page": 87,
        "old": 25,
        "id": "A25-022",
        "source_ids": [
          "A25-022"
        ],
        "anchor": "A25-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "На отдельном тестовом накопителе выполнить серию остановок процесса и контролируемых отключений питания в разные моменты записи. Проверить целостность, незавершённые команды, время восстановления и отсутствие повторного опасного действия. Не проводить этот опыт на единственной копии ценных данных.",
        "y": 605.3275146484375,
        "page": 87,
        "old": 25,
        "id": "A25-023",
        "source_ids": [
          "A25-023"
        ],
        "anchor": "A25-023",
        "content_status": "active"
      },
      {
        "kind": "lead",
        "text": "Текущий снимок, журнал управления и подробная история требуют разных способов записи и хранения. Для первой версии это логическое разделение можно реализовать в одном локальном хранилище.",
        "id": "I19-001",
        "old": "I19",
        "source_ids": [
          "I19-001"
        ],
        "anchor": "I19-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Три режима данных",
        "id": "I19-002",
        "old": "I19",
        "source_ids": [
          "I19-002"
        ],
        "anchor": "I19-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Актуальный снимок нужен для быстрых решений. Он находится в памяти, содержит свежесть и ссылки на исходные записи. После перезапуска восстановленное значение не объявляется свежим без проверки.",
        "id": "I19-003",
        "old": "I19",
        "source_ids": [
          "I19-003"
        ],
        "anchor": "I19-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Журнал решений и конфигурации сохраняет права, связи объектов, версии, команды и их результаты. Для обещания «надёжно принято» нужна соответствующая транзакционная запись. Нельзя считать ответ успешным до шага, сохранность которого обещана пользователю.",
        "id": "I19-004",
        "old": "I19",
        "source_ids": [
          "I19-004"
        ],
        "anchor": "I19-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "История измерений допускает агрегирование и ограниченные потери согласно профилю. Аудио, видео и большие диагностические потоки имеют отдельную политику и по умолчанию не записываются постоянно.",
        "id": "I19-005",
        "old": "I19",
        "source_ids": [
          "I19-005"
        ],
        "anchor": "I19-005",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I19",
        "caption": "Одна первичная запись порождает несколько представлений. Экспорт для обучения не становится новым источником истины для текущего управления.",
        "id": "I19-006",
        "old": "I19",
        "source_ids": [
          "I19-006"
        ],
        "anchor": "I19-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Выбор хранения для первого экземпляра",
        "id": "I19-007",
        "old": "I19",
        "source_ids": [
          "I19-007"
        ],
        "anchor": "I19-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для конфигурации и журнала исходным кандидатом остаётся SQLite с ограниченным писателем и явной политикой синхронизации. WAL поддерживает параллельное чтение и запись, но не отменяет требования к файловой системе и согласованному резервированию. [51] Альтернативу на Rust, например выбранное встроенное key-value хранилище, сравниваем по реальным транзакциям и восстановлению, а не по языку реализации.",
        "id": "I19-008",
        "old": "I19",
        "source_ids": [
          "I19-008"
        ],
        "anchor": "I19-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Большие записи можно вынести в последовательные чанки с индексом. MCAP задаёт контейнер для каналов и сообщений с временными метками; он полезен как кандидат формата записи/повтора. [108] Arrow подходит для столбцового обмена пакетами данных в аналитике. [109] Ни тот ни другой не обязателен для каждого маленького KQIP-сообщения.",
        "id": "I19-009",
        "old": "I19",
        "source_ids": [
          "I19-009"
        ],
        "anchor": "I19-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первой комнаты достаточно одного локального хранилища и простого экспортируемого журнала. Добавление отдельного аналитического формата оправдано измеренным объёмом или удобством исследования.",
        "id": "I19-010",
        "old": "I19",
        "source_ids": [
          "I19-010"
        ],
        "anchor": "I19-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Сколько стоит «сохранять всё»",
        "id": "I19-011",
        "old": "I19",
        "source_ids": [
          "I19-011"
        ],
        "anchor": "I19-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Четыре аудиоканала по 16 000 отсчётов/с и 16 бит дают KQMATH047TOKEN байт/с — около 11,06 ГБ за сутки без служебных расходов. Сто событий в секунду по 200 байт — 1,728 ГБ за сутки. Это расчётные объёмы в десятичных единицах, не показатели компрессии.",
        "id": "I19-012",
        "old": "I19",
        "source_ids": [
          "I19-012"
        ],
        "anchor": "I19-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Храним краткое кольцевое окно только при необходимости, задаём сроки удаления и резерв под критический журнал. Заполненный диск не должен приводить к бесконечному повтору команды. У пользователя есть отдельные переключатели для хранения аудио, диагностических данных и участия в обучении.",
        "id": "I19-013",
        "old": "I19",
        "source_ids": [
          "I19-013"
        ],
        "anchor": "I19-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Совместимость KQIP",
        "id": "I19-014",
        "old": "I19",
        "source_ids": [
          "I19-014"
        ],
        "anchor": "I19-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Новые логические сущности не требуют ломать транспорт. Предлагаем согласование поддерживаемых дескрипторов наблюдений и результатов. Канал может объявить единицу, шкалу времени, параметры измерения и ограничения; отдельные сообщения ссылаются на него компактно.",
        "id": "I19-015",
        "old": "I19",
        "source_ids": [
          "I19-015"
        ],
        "anchor": "I19-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Неизвестный тип не превращается в ближайший знакомый. Неизвестное критическое поле отклоняет действие; необязательную диагностику можно пропустить только по явному правилу. Логическая запись и бинарное кодирование версионируются раздельно: расширенный паспорт наблюдения не меняет молча профиль KQIP 0.2; малый вектор 0.1 оставлен историческим примером. Новая редакция кодека требует согласованных тестовых векторов.",
        "id": "I19-016",
        "old": "I19",
        "edited": true,
        "source_ids": [
          "I19-016"
        ],
        "anchor": "I19-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Дубли, порядки и перезапуск",
        "id": "I19-017",
        "old": "I19",
        "source_ids": [
          "I19-017"
        ],
        "anchor": "I19-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Ключ наблюдения включает канал, эпоху перезапуска и последовательность. Дубликат не создаёт второе физическое событие. При пересборке производных данных версия преобразования добавляет новую сущность, а не изменяет идентичность исходной записи.",
        "id": "I19-018",
        "old": "I19",
        "source_ids": [
          "I19-018"
        ],
        "anchor": "I19-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "После перезапуска ядро восстанавливает журнал принятых операций, узнаёт фактические состояния устройств и только затем возобновляет разрешённые процессы. Два восстановленных сервера не получают одновременно право на один выход: у установки остаётся идентичность и процедура передачи полномочий.",
        "id": "I19-019",
        "old": "I19",
        "source_ids": [
          "I19-019"
        ],
        "anchor": "I19-019",
        "content_status": "active"
      },
      {
        "kind": "quote",
        "text": "Быстрое состояние, долговечный журнал и большой архив имеют разные требования. Их разделение — логическое; оно не обязывает покупать сервер СУБД или разворачивать микросервисы.",
        "id": "I19-020",
        "old": "I19",
        "source_ids": [
          "I19-020"
        ],
        "anchor": "I19-020",
        "content_status": "active"
      },
      {
        "id": "R39-001",
        "kind": "h2",
        "text": "Нет места на диске — нет обещания ACCEPTED",
        "source_ids": [
          "R39-001"
        ],
        "anchor": "R39-001",
        "content_status": "active"
      },
      {
        "id": "R39-002",
        "kind": "p",
        "text": "Бюджет диска включает критическую БД, WAL и checkpoint, экспорт, временное обновление, две версии моделей и резервную копию. Начальный резерв для E0 выбирается как большее из 1 ГиБ и удвоенного максимального текущего служебного файла; это порог остановки тяжёлых задач, не универсальная гарантия. Перед commit всё равно обрабатываются ENOSPC, I/O и fsync-ошибки. Неподтверждённая запись не выдаёт ACCEPTED.",
        "source_ids": [
          "R39-002"
        ],
        "anchor": "R39-002",
        "content_status": "active"
      },
      {
        "id": "R39-003",
        "kind": "p",
        "text": "При нехватке места сначала останавливаются raw-запись, обучение, экспорт и OTA. Новые сетевые команды, которым нельзя обеспечить обещанный журнал, отклоняются явно. Локальная кнопка с отдельным питанием остаётся доступной. Вариант временного сетевого управления без долговечности потребовал бы нового явного договора и не включён в E0.",
        "source_ids": [
          "R39-003"
        ],
        "anchor": "R39-003",
        "content_status": "active"
      },
      {
        "id": "R39-004",
        "kind": "p",
        "text": "Минимальные основания решения сохраняются в той же критической транзакции: использованные значения и интервалы, статусы, версии, активные ограничения и ссылки на полный raw. Удаление телеметрии тогда не уничтожает смысл команды. Исчезнувшая по retention подробная запись отмечается «истёк срок хранения», а не «повреждение». Записи журнала неизменяемы; версия схемы и upcaster дают новое представление, не переписывая прошлое.",
        "source_ids": [
          "R39-004"
        ],
        "anchor": "R39-004",
        "content_status": "active"
      },
      {
        "id": "R39-005",
        "kind": "h2",
        "text": "Ресурс накопителя считаем по записи, а не по JSON",
        "source_ids": [
          "R39-005"
        ],
        "anchor": "R39-005",
        "content_status": "active"
      },
      {
        "id": "R39-006",
        "kind": "p",
        "text": "Если ради 200 байт отдельно записать страницу 4 КиБ в WAL, а затем ещё раз при checkpoint, теоретический пример даёт примерно 40-кратное отношение. Это не измеренный коэффициент SQLite на нашей схеме и не полный NAND write amplification. Индексы, batch, checkpoint, файловая система и контроллер меняют результат. Проверяем host writes, объём WAL, реальную частоту commit и доступную телеметрию SSD; сравниваем с ресурсом конкретного накопителя. [51]",
        "source_ids": [
          "R39-006"
        ],
        "anchor": "R39-006",
        "content_status": "active"
      },
      {
        "id": "R39-007",
        "kind": "p",
        "text": "Непрерывное аудио по умолчанию не сохраняется. Для диагностики — ограниченный RAM-буфер и явное согласие на эпизод. Транскрипты и производные признаки имеют отдельные сроки хранения. Лицензия весов, голосов TTS и runtime записывается в манифест; язык реализации сам по себе не решает эти ограничения.",
        "source_ids": [
          "R39-007"
        ],
        "anchor": "R39-007",
        "content_status": "active"
      }
    ],
    "origins": [
      "A25",
      "I19"
    ],
    "question": "Что хранить, чтобы объяснить вчерашнее действие?",
    "intro": "Одного текущего состояния недостаточно. Чтобы понять, почему включился свет, нужны наблюдения, версия модели, действовавшие ограничения, причина команды и её результат. Но хранить каждый звук и каждый отсчёт навсегда тоже не нужно.\n\nРазделим быстрый снимок в памяти, долговечный журнал управления и крупные исследовательские записи. У каждого свой срок хранения и способ восстановления. Повторное проигрывание истории будет отдельным режимом, который не имеет права незаметно посылать живые команды.",
    "terms": [
      "outbox, сохранённая очередь ещё не завершённой отправки"
    ],
    "id": "ch-39",
    "slug": "chapter-39"
  },
  {
    "number": 40,
    "title": "Приложение, которое объясняет",
    "group": "Интерфейс",
    "units": [
      {
        "kind": "lead",
        "text": "Приложение показывает неопределённость состояния и помогает восстановить управление. Внешний вид кнопки должен соответствовать известному результату, включая отсутствие подтверждения.",
        "y": 137.3555450439453,
        "page": 88,
        "old": 26,
        "id": "A26-001",
        "source_ids": [
          "A26-001"
        ],
        "anchor": "A26-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "KMP позволяет разделять общую логику между Android и iOS, сохраняя платформенные интеграции там, где они действительно различаются. В Koobique общими могут быть доменные состояния, сценарии подключения, обработка команд и представление данных. BLE, защищённое хранилище, разрешения, аудио и жизненный цикл требуют платформенных адаптеров. [54]",
        "y": 188.23130798339844,
        "page": 88,
        "old": 26,
        "id": "A26-002",
        "source_ids": [
          "A26-002"
        ],
        "anchor": "A26-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Общий код и Rust",
        "y": 276.7109680175781,
        "page": 88,
        "old": 26,
        "id": "A26-003",
        "source_ids": [
          "A26-003"
        ],
        "anchor": "A26-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Общий Rust-код полезен для контрактов и проверок, но стандартный UniFFI генерирует Kotlin/JVM и Swift, а не готовый commonMain KMP. Кандидаты: expect/actual над этими двумя границами или Gobley с KMP-привязками. До выбора нужен небольшой опыт на Android и iPhone: cancellation, callbacks, многопоточность, память и доставка ошибок. Слой FFI не должен открывать внутренние указатели Core. [155][154]",
        "y": 303.49847412109375,
        "page": 88,
        "old": 26,
        "id": "A26-004",
        "source_ids": [
          "A26-004"
        ],
        "anchor": "A26-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Альтернативный путь — отдельный Kotlin-кодек по той же спецификации и golden vectors. Он уменьшает FFI, но создаёт две реализации. Выбор делается после небольшого proof-of-concept: подключение, одно сообщение, отмена соединения и обновление библиотеки на обеих платформах. Не стоит включать всю Rust-сеть в приложение только ради сокращения ста строк Kotlin.",
        "y": 371.30401611328125,
        "page": 88,
        "old": 26,
        "id": "A26-005",
        "source_ids": [
          "A26-005"
        ],
        "anchor": "A26-005",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A33",
        "caption": "Предлагаемые границы приложения. Макет экрана иллюстрирует состояния интерфейса, а не задаёт окончательную дизайн-систему KMP.",
        "y": 56.69291687011719,
        "page": 89,
        "old": 26,
        "id": "A26-006",
        "source_ids": [
          "A26-006"
        ],
        "anchor": "A26-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Главный экран и поведение команды",
        "y": 431.1031188964844,
        "page": 89,
        "old": 26,
        "id": "A26-007",
        "source_ids": [
          "A26-007"
        ],
        "anchor": "A26-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Плитка света показывает подтверждённое состояние. После нажатия появляется «выполняется». При принятии без результата — «ожидается подтверждение». При обрыве — «результат неизвестен», а не автоматический возврат в OFF. Повтор использует тот же command_id, когда это продолжение прежней попытки.",
        "y": 457.890625,
        "page": 89,
        "old": 26,
        "id": "A26-008",
        "source_ids": [
          "A26-008"
        ],
        "anchor": "A26-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Карточка температуры показывает возраст данных; карточка присутствия умеет UNKNOWN. Недоступное устройство не исчезает из списка и не выглядит выключенным. История решения доступна через действие «почему»: использованные условия, источник и фактический результат.",
        "y": 525.6962280273438,
        "page": 89,
        "old": 26,
        "id": "A26-009",
        "source_ids": [
          "A26-009"
        ],
        "anchor": "A26-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Подключение без лабиринта настроек",
        "y": 599.1378173828125,
        "page": 89,
        "old": 26,
        "id": "A26-010",
        "source_ids": [
          "A26-010"
        ],
        "anchor": "A26-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Пользователь выбирает «добавить станцию», сканирует QR, подтверждает физическое владение и передаёт сеть. Экран различает ошибочный пароль, отсутствие подходящего диапазона, отказ локальной сети и незавершённую привязку. Подключение устройства — отдельный похожий сценарий, но его результатом является принадлежность конкретной станции.",
        "y": 625.9253540039062,
        "page": 89,
        "old": 26,
        "id": "A26-011",
        "source_ids": [
          "A26-011"
        ],
        "anchor": "A26-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Разрешения запрашиваются в момент понятной необходимости. Android имеет отдельные требования к Bluetooth-разрешениям, а iOS — к доступу в локальную сеть. Приложение должно объяснять, что оно ищет локальные устройства, а не требовать всё при первом запуске. [56][57]",
        "y": 708.7689208984375,
        "page": 89,
        "old": 26,
        "id": "A26-012",
        "source_ids": [
          "A26-012"
        ],
        "anchor": "A26-012",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Хранение ключей",
        "y": 56.314456939697266,
        "page": 90,
        "old": 26,
        "id": "A26-013",
        "source_ids": [
          "A26-013"
        ],
        "anchor": "A26-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Android Keystore и Apple Keychain дают платформенные механизмы хранения секретов. Их доступность и ограничения используются через адаптеры, а не заменяются файлом с ключом в обычных preferences. Экспорт резервного ключа владельца — отдельная операция с явным предупреждением, не часть стандартного диагностического пакета. [71][97]",
        "y": 83.10196685791016,
        "page": 90,
        "old": 26,
        "id": "A26-014",
        "source_ids": [
          "A26-014"
        ],
        "anchor": "A26-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Закреплённая идентичность станции хранится вместе с инсталляцией. Приложение не доверяет новому сертификату только потому, что он пришёл с прежнего IP. Смена ключа требует подписанного перехода или процедуры восстановления с физическим подтверждением.",
        "y": 150.9075164794922,
        "page": 90,
        "old": 26,
        "id": "A26-015",
        "source_ids": [
          "A26-015"
        ],
        "anchor": "A26-015",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Фоновый режим",
        "y": 224.34915161132812,
        "page": 90,
        "old": 26,
        "id": "A26-016",
        "source_ids": [
          "A26-016"
        ],
        "anchor": "A26-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Нельзя рассчитывать, что приложение постоянно держит BLE, микрофон и сетевой сокет в фоне одинаково на Android и iOS. Android документирует специальные способы фоновой BLE-работы; другие функции имеют собственный жизненный цикл. Поэтому автоматизации живут на станции, а не в мобильном фоне. [83]",
        "y": 251.13670349121094,
        "page": 90,
        "old": 26,
        "id": "A26-017",
        "source_ids": [
          "A26-017"
        ],
        "anchor": "A26-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Push через внешние службы может быть удобным дополнением, но не основанием безопасности. При отсутствии интернета локальная карточка состояния работает, а внешнее push-уведомление может задержаться. Уведомление об опасном процессе не заменяет локальную защиту оборудования.",
        "y": 318.94219970703125,
        "page": 90,
        "old": 26,
        "id": "A26-018",
        "source_ids": [
          "A26-018"
        ],
        "anchor": "A26-018",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Работа с голосом на телефоне",
        "y": 392.38385009765625,
        "page": 90,
        "old": 26,
        "id": "A26-019",
        "source_ids": [
          "A26-019"
        ],
        "anchor": "A26-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "На первом этапе используется кнопка записи. Аудио передаётся в станцию по защищённой сессии и обрабатывается локально. Это даёт RU/EN-команды без немедленной интеграции микрофонов в куб. Альтернатива — локальное распознавание на телефоне, если выбранный runtime и политика платформы позволяют полностью офлайн-режим.",
        "y": 419.1713562011719,
        "page": 90,
        "old": 26,
        "id": "A26-020",
        "source_ids": [
          "A26-020"
        ],
        "anchor": "A26-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Приложение показывает распознанный объект и при необходимости уточнение. Различие «свет» / «подсветка» разрешается через модель комнаты. Нельзя молча выполнить случайное ближайшее имя только потому, что STT дал похожую строку.",
        "y": 486.9769287109375,
        "page": 90,
        "old": 26,
        "id": "A26-021",
        "source_ids": [
          "A26-021"
        ],
        "anchor": "A26-021",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Сборка и установка личного приложения",
        "y": 545.380615234375,
        "page": 90,
        "old": 26,
        "id": "A26-022",
        "source_ids": [
          "A26-022"
        ],
        "anchor": "A26-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Android-экземпляр должен иметь воспроизводимую подписанную сборку и сохранённый ключ обновления. На iOS заранее проверяются Xcode, provisioning и ограничения выбранного типа аккаунта. Сам факт наличия MacBook не означает бессрочную установку любой development-сборки; текущие условия проверяются по документации Apple. [58]",
        "y": 572.1680908203125,
        "page": 90,
        "old": 26,
        "id": "A26-023",
        "source_ids": [
          "A26-023"
        ],
        "anchor": "A26-023",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Приёмка UX",
        "y": 657.3240356445312,
        "page": 90,
        "old": 26,
        "id": "A26-024",
        "source_ids": [
          "A26-024"
        ],
        "anchor": "A26-024",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Дать второму человеку подключить тестовый узел без устной подсказки. Преднамеренно ввести неверный пароль, отказать в разрешении и прервать процесс. Успех — не только подключение, но и понятный способ продолжить без сброса всей инсталляции.",
        "y": 675.7852172851562,
        "page": 90,
        "old": 26,
        "id": "A26-025",
        "source_ids": [
          "A26-025"
        ],
        "anchor": "A26-025",
        "content_status": "active"
      },
      {
        "kind": "lead",
        "text": "Мобильное приложение связывает состояние переключателя с целью пользователя, неопределённостью и пределами разрешённой автоматизации.",
        "id": "I21-001",
        "old": "I21",
        "source_ids": [
          "I21-001"
        ],
        "anchor": "I21-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Что видит пользователь",
        "id": "I21-002",
        "old": "I21",
        "source_ids": [
          "I21-002"
        ],
        "anchor": "I21-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Экран комнаты содержит актуальные измерения с признаком свежести, оценку присутствия, фактические состояния выходов и выбранный профиль. Системное «неизвестно» отображается отдельно от OFF. При команде показывается продвижение: принята, выход подтвердил, физический результат проверяется.",
        "id": "I21-003",
        "old": "I21",
        "source_ids": [
          "I21-003"
        ],
        "anchor": "I21-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для каждой автоматизации доступна краткая причина и возможность отключить её без удаления устройств. Подробный режим раскрывает наблюдения, версии и ограничение, которое изменило решение. Это проектные требования интерфейса; внутренние правила разработки KMP будут согласованы с документами пользователя, а не выдуманы вместо них.",
        "id": "I21-004",
        "old": "I21",
        "source_ids": [
          "I21-004"
        ],
        "anchor": "I21-004",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I21",
        "caption": "Одинаковое действие может прийти из кнопки приложения, речи или автоматизации. Все пути сходятся в едином исполнителе; голос не получает отдельный обход ограничений.",
        "id": "I21-005",
        "old": "I21",
        "source_ids": [
          "I21-005"
        ],
        "anchor": "I21-005",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "RU/EN и неоднозначность",
        "id": "I21-006",
        "old": "I21",
        "source_ids": [
          "I21-006"
        ],
        "anchor": "I21-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "«Включи свет», «включи освещение» и “turn on the lights” могут соответствовать одному намерению. «Подсветка» может быть отдельным физическим объектом. Поэтому словарь синонимов связывается с возможностями и пространствами установки, а не глобально заменяет каждое слово «свет» на ID первого реле.",
        "id": "I21-007",
        "old": "I21",
        "source_ids": [
          "I21-007"
        ],
        "anchor": "I21-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "При нескольких подходящих объектах приложение задаёт короткий вопрос: «Основной свет или подсветку стола?» Ответ привязан к текущему запросу и ограничен по времени. Если за это время изменились права или удалилось устройство, старый ответ не запускает прежнее действие.",
        "id": "I21-008",
        "old": "I21",
        "source_ids": [
          "I21-008"
        ],
        "anchor": "I21-008",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Локальный тракт речи",
        "id": "I21-009",
        "old": "I21",
        "source_ids": [
          "I21-009"
        ],
        "anchor": "I21-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Последовательность сохраняется: захват, определение речевого участка, распознавание, интерпретация, проверка прав, исполнение и ответ. На первом этапе телефон предоставляет микрофон, а станция или существующий ПК выполняет выбранные локальные модели. Встроенный голосовой модуль куба добавляется после испытания акустики и вычислительной нагрузки; аппаратные варианты разобраны в главах 41–42.",
        "id": "I21-010",
        "old": "I21",
        "edited": true,
        "source_ids": [
          "I21-010"
        ],
        "anchor": "I21-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Языковая модель нужна для вариативных формулировок и диалога, но не для точного сравнения времени или проверки электрического диапазона. Сложная фраза сначала превращается в атомарные намерения. Непонятный фрагмент не заменяется наиболее удобным действием.",
        "id": "I21-011",
        "old": "I21",
        "source_ids": [
          "I21-011"
        ],
        "anchor": "I21-011",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Что считать успехом голоса",
        "id": "I21-012",
        "old": "I21",
        "source_ids": [
          "I21-012"
        ],
        "anchor": "I21-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Точность распознавания текста недостаточна. Ошибка в одном слове может перепутать основное освещение и подсветку; грамматическая ошибка может не менять действие. Проверяем точность конечного намерения, долю ненужных уточнений, ошибочные действия и время до физического результата.",
        "id": "I21-013",
        "old": "I21",
        "source_ids": [
          "I21-013"
        ],
        "anchor": "I21-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Отдельные наборы: русский, английский, смешанная речь, шум вентиляторов, похожие имена, отрицания и составные запросы. Текст из телевизора или воспроизведённой записи не становится командой владельца только потому, что распознаётся уверенно. Риск голосового запуска и методы подтверждения задаются по классу действия.",
        "id": "I21-014",
        "old": "I21",
        "source_ids": [
          "I21-014"
        ],
        "anchor": "I21-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Обучение пользовательскому смыслу",
        "id": "I21-015",
        "old": "I21",
        "source_ids": [
          "I21-015"
        ],
        "anchor": "I21-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Человек может назвать настольную лампу «моя подсветка». Система сохраняет явный локальный псевдоним после подтверждения. Повторяющееся исправление может породить предложение обновить словарь. Не меняем автоматически нагрузку, права и глобальный смысл команд для остальных пользователей.",
        "id": "I21-016",
        "old": "I21",
        "source_ids": [
          "I21-016"
        ],
        "anchor": "I21-016",
        "content_status": "active"
      },
      {
        "kind": "quote",
        "text": "Приложение связывает человеческую цель с физическим объектом. AI помогает понять речь; авторизация, неоднозначность и проверка результата остаются видимыми и управляемыми.",
        "id": "I21-017",
        "old": "I21",
        "source_ids": [
          "I21-017"
        ],
        "anchor": "I21-017",
        "content_status": "active"
      },
      {
        "id": "R40-001",
        "kind": "h2",
        "text": "Обнаружение без подмены доверия",
        "source_ids": [
          "R40-001"
        ],
        "anchor": "R40-001",
        "content_status": "active"
      },
      {
        "id": "R40-002",
        "kind": "p",
        "text": "Сервис объявляется как _koobique._tcp; TXT содержит только ID установки, поддерживаемый профиль и подсказку идентичности. Отпечаток из mDNS не является доверенным pin: любой сосед может объявить похожую запись. Android использует NsdManager, iOS — Bonjour/NWBrowser с необходимыми разрешениями Local Network и списком сервисов. Отказ разрешения показывается отдельно от отсутствия Hub. MulticastLock не назначается всем сборкам автоматически: необходимость зависит от того, используется системное NSD или собственные multicast-сокеты. [159][57]",
        "source_ids": [
          "R40-002"
        ],
        "anchor": "R40-002",
        "content_status": "active"
      },
      {
        "id": "R40-003",
        "kind": "p",
        "text": "Один элемент управления показывает текущий snapshot, рядом — выполнение конкретной команды. Поздний APPLIED(A) обновляет историю A, но не заменяет более новый OFF. При UNKNOWN текст объясняет действие: «не удалось подтвердить; проверить состояние», а не «включено». После истечения запроса новая попытка требует текущих условий и нового ID.",
        "source_ids": [
          "R40-003"
        ],
        "anchor": "R40-003",
        "content_status": "active"
      },
      {
        "id": "R40-004",
        "kind": "p",
        "text": "В приложении предусмотрены VoiceOver/TalkBack, масштабирование текста и обозначения состояния не только цветом. «Ручной режим» сопровождается ответом, когда вернётся автоматика. Для E0: «до команды “вернуть автоматику”». Технические boot/fence/lease раскрываются только в диагностике, не перегружают основной экран.",
        "source_ids": [
          "R40-004"
        ],
        "anchor": "R40-004",
        "content_status": "active"
      },
      {
        "id": "R40-005",
        "kind": "p",
        "text": "Общий микрофон телефона в первом опыте передаёт аудио на локальный speech-worker Hub. Системное распознавание iOS/Android не считается автоматически офлайн; использование Apple Speech требует проверки supportsOnDeviceRecognition и requiresOnDeviceRecognition для конкретного языка. При отсутствии локального режима нет скрытого перехода в облако. [176]",
        "source_ids": [
          "R40-005"
        ],
        "anchor": "R40-005",
        "content_status": "active"
      },
      {
        "id": "R3-40-BOUNDARY-H",
        "kind": "h2",
        "text": "Намерение приложения и команда устройству",
        "source_ids": [],
        "anchor": "R3-40-BOUNDARY-H",
        "content_status": "r3_addition"
      },
      {
        "id": "R3-40-BOUNDARY-P",
        "kind": "p",
        "text": "Приложение отправляет пользовательское намерение в Core. Ядро проверяет права, разрешает объект, фиксирует основания и создаёт одну или несколько команд KQIP для узлов. Поэтому пользовательский запрос и попытка исполнения имеют разные идентичности и историю. API приложения должен описывать их связь, чтение snapshot и обновлений, отзыв сессии, ошибки и восстановление подписки. Перенос KQIP в телефон сам по себе этих операций не определяет; до реализации клиента нужен отдельный проверяемый контракт.",
        "source_ids": [],
        "anchor": "R3-40-BOUNDARY-P",
        "content_status": "r3_addition"
      }
    ],
    "origins": [
      "A26",
      "I21"
    ],
    "question": "Как показать человеку не только кнопку, но и смысл происходящего?",
    "intro": "Хороший экран отвечает на несколько коротких вопросов: что система сейчас знает, почему выбрано действие, подтверждено ли оно и как вернуть управление человеку. Если результат неизвестен, это должно быть видно без чтения технического журнала.\n\nKMP объединит часть логики Android и iOS, но не отменит различия разрешений, фоновой работы и системного хранилища ключей. Точные библиотеки будут выбраны по отдельным правилам проекта; здесь задаём поведение и границы интерфейса.",
    "id": "ch-40",
    "slug": "chapter-40"
  },
  {
    "number": 41,
    "title": "Голос: от звука к намерению",
    "group": "Голос",
    "units": [
      {
        "kind": "lead",
        "text": "Для команды «включи свет» важнее правильно выбрать объект, чем сгенерировать длинный красивый ответ.",
        "y": 168.675537109375,
        "page": 91,
        "old": 27,
        "id": "A27-001",
        "source_ids": [
          "A27-001"
        ],
        "anchor": "A27-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Голосовой тракт состоит из захвата, обнаружения речи, распознавания, разрешения намерения, проверки полномочий, исполнения и ответа. Каждый этап имеет отдельную ошибку и метрику. Объединение всего в один запрос к большой модели затрудняет проверку и делает простую команду дороже.",
        "y": 219.78143310546875,
        "page": 91,
        "old": 27,
        "id": "A27-002",
        "source_ids": [
          "A27-002"
        ],
        "anchor": "A27-002",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A34",
        "caption": "Последовательность обработки голоса. Исполнитель команд тот же, что у приложения и автоматизаций.",
        "y": 299.9540710449219,
        "page": 91,
        "old": 27,
        "id": "A27-003",
        "source_ids": [
          "A27-003"
        ],
        "anchor": "A27-003",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Кандидаты распознавания",
        "y": 56.314456939697266,
        "page": 92,
        "old": 27,
        "id": "A27-004",
        "source_ids": [
          "A27-004"
        ],
        "anchor": "A27-004",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Стек",
            "Когда полезен",
            "Что проверить"
          ],
          [
            "whisper.cpp, multilingual-модель",
            "Универсальные RU/EN-фразы и разные формулировки",
            "Задержка, память, ложный текст на тишине, выбранный backend"
          ],
          [
            "sherpa-onnx",
            "Потоковые и специализированные speech-пайплайны",
            "Конкретная модель, лицензия, streaming и endpointing"
          ],
          [
            "Vosk",
            "Небольшой офлайн-словарь и грамматика команд",
            "Качество имён устройств и смешанной речи"
          ],
          [
            "Candle / собственный Rust-worker",
            "Более единая среда Rust",
            "Поддержка нужной модели и оптимизаций"
          ]
        ],
        "y": 85.43,
        "page": 92,
        "old": 27,
        "id": "A27-005",
        "source_ids": [
          "A27-005"
        ],
        "anchor": "A27-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Эти проекты предоставляют разные реализации и модели. Для русского нельзя случайно выбрать англоязычную.en-модель и затем оценивать весь подход как неработающий. Скорость зависит от весов, квантования, CPU/GPU и длины аудио; готовые репозитории служат основой эксперимента, а не обещанием конкретной задержки. [61] [62][73][99]",
        "y": 286.86944580078125,
        "page": 92,
        "old": 27,
        "id": "A27-006",
        "source_ids": [
          "A27-006"
        ],
        "anchor": "A27-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "VAD и конец фразы",
        "y": 382.6386413574219,
        "page": 92,
        "old": 27,
        "id": "A27-007",
        "source_ids": [
          "A27-007"
        ],
        "anchor": "A27-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "VAD определяет, есть ли речь, а не смысл команды. Silero VAD — один из доступных кандидатов. Он помогает не отправлять в STT непрерывную тишину и шум, но может обрезать тихое начало или конец. Нужен небольшой pre-roll и согласованная задержка завершения. [63]",
        "y": 409.6562805175781,
        "page": 92,
        "old": 27,
        "id": "A27-008",
        "source_ids": [
          "A27-008"
        ],
        "anchor": "A27-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Конец фразы — компромисс: слишком короткая пауза обрежет «включи… подсветку за монитором», слишком длинная сделает интерфейс медленным. Отдельно измеряются время после окончания речи и полное время до физического результата. Пользователь воспринимает именно последнюю цепочку.",
        "y": 482.0602111816406,
        "page": 92,
        "old": 27,
        "id": "A27-009",
        "source_ids": [
          "A27-009"
        ],
        "anchor": "A27-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Намерение и объект",
        "y": 561.8544311523438,
        "page": 92,
        "old": 27,
        "id": "A27-010",
        "source_ids": [
          "A27-010"
        ],
        "anchor": "A27-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "После текста формируется структура: действие, объект, помещение, параметры и контекст. Слова «включи свет» и «включи освещение» могут указывать на основной светильник. «Включи подсветку» указывает на другой capability или сцену, если она существует. Если в комнате две подсветки, требуется уточнение.",
        "y": 588.8720092773438,
        "page": 92,
        "old": 27,
        "id": "A27-011",
        "source_ids": [
          "A27-011"
        ],
        "anchor": "A27-011",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A35",
        "caption": "Синонимы и неоднозначность. Приложение и станция используют один реестр объектов и комнат.",
        "y": 56.69291687011719,
        "page": 93,
        "old": 27,
        "id": "A27-012",
        "source_ids": [
          "A27-012"
        ],
        "anchor": "A27-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первого экземпляра полезен ограниченный intent-parser со словарём синонимов, именами устройств и шаблонами параметров. Он проще тестируется и не требует большой LLM. Контекст включает выбранную комнату, источник микрофона и текущий диалог уточнения. Предположение о комнате не должно быть скрытым.",
        "y": 377.5941467285156,
        "page": 93,
        "old": 27,
        "id": "A27-013",
        "source_ids": [
          "A27-013"
        ],
        "anchor": "A27-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Где уместна языковая модель",
        "y": 457.3883056640625,
        "page": 93,
        "old": 27,
        "id": "A27-014",
        "source_ids": [
          "A27-014"
        ],
        "anchor": "A27-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "LLM может преобразовать разнообразную фразу в ограниченную структуру или объяснить причину действия. Ей передаётся только разрешённый каталог объектов и операций. Выход валидируется по схеме, правам, диапазонам и актуальному состоянию. Никакого shell, SQL или прямого GPIO из сгенерированного текста.",
        "y": 484.40594482421875,
        "page": 93,
        "old": 27,
        "id": "A27-015",
        "source_ids": [
          "A27-015"
        ],
        "anchor": "A27-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "llama.cpp и Candle — варианты локального вывода, но наличие такого runtime не создаёт безопасный агент само по себе. Для команды с побочным эффектом особенно важно отделить предложение от исполнения. [66][73]",
        "y": 556.8098754882812,
        "page": 93,
        "old": 27,
        "id": "A27-016",
        "source_ids": [
          "A27-016"
        ],
        "anchor": "A27-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Ответ и озвучивание",
        "y": 620.6290893554688,
        "page": 93,
        "old": 27,
        "id": "A27-017",
        "source_ids": [
          "A27-017"
        ],
        "anchor": "A27-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Короткий ответ сообщает результат: «Основной свет включён» после подтверждения; «Команда отправлена, подтверждение не пришло» при неопределённости. Не следует всегда говорить «готово» сразу после приёма звука. Для простых случаев достаточно заранее подготовленных фраз; локальный TTS добавляет гибкость.",
        "y": 647.6466674804688,
        "page": 93,
        "old": 27,
        "id": "A27-018",
        "source_ids": [
          "A27-018"
        ],
        "anchor": "A27-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Piper — кандидат синтеза, при этом лицензия программы и конкретных голосов проверяется отдельно. Первые RU/EN-ответы можно сделать текстовыми, чтобы не смешивать ошибки распознавания и акустическое эхо динамика. [65]",
        "y": 720.0505981445312,
        "page": 93,
        "old": 27,
        "id": "A27-019",
        "source_ids": [
          "A27-019"
        ],
        "anchor": "A27-019",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Корпус команд для проверки",
        "y": 56.314456939697266,
        "page": 94,
        "old": 27,
        "id": "A27-020",
        "source_ids": [
          "A27-020"
        ],
        "anchor": "A27-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Набор включает разные голоса, темп, расстояния, шум, отрицания и похожие имена: «не включай свет», «выключи подсветку», «включи свет в коридоре», «set the desk light to…», «turn off the backlight». Отдельно записываются обычный разговор и телевизор/ музыка, которые не должны запускать действия.",
        "y": 83.33208465576172,
        "page": 94,
        "old": 27,
        "id": "A27-021",
        "source_ids": [
          "A27-021"
        ],
        "anchor": "A27-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Главные показатели: доля правильно выбранных действий и объектов, ложные исполнения, запросы уточнения и время до результата. WER текста полезен, но не равен безопасности: одна ошибка в слове «не» опаснее нескольких ошибок в необязательной части фразы.",
        "y": 155.73602294921875,
        "page": 94,
        "old": 27,
        "id": "A27-022",
        "source_ids": [
          "A27-022"
        ],
        "anchor": "A27-022",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Мощность и конкуренция",
        "y": 235.53021240234375,
        "page": 94,
        "old": 27,
        "id": "A27-023",
        "source_ids": [
          "A27-023"
        ],
        "anchor": "A27-023",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Speech-worker имеет ограничение числа запросов. Устройство сначала возвращает короткое подтверждение захвата, затем результат. Большая модель не должна задерживать выключение реле или блокировать БД. При перегрузке голос может перейти на меньшую модель или сообщить о временной недоступности; уже работающие правила сохраняются.",
        "y": 262.5478515625,
        "page": 94,
        "old": 27,
        "id": "A27-024",
        "source_ids": [
          "A27-024"
        ],
        "anchor": "A27-024",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Первое сравнение моделей",
        "y": 367.1966552734375,
        "page": 94,
        "old": 27,
        "id": "A27-025",
        "source_ids": [
          "A27-025"
        ],
        "anchor": "A27-025",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "На имеющемся ПК записать не менее нескольких десятков RU/EN-команд и негативных примеров. Прогнать два размера Whisper либо Whisper и Vosk/sherpa. Сохранить точность намерений, задержку после конца речи и память. Только после этого выбирать постоянный вычислитель для голоса.",
        "y": 385.6578674316406,
        "page": 94,
        "old": 27,
        "id": "A27-026",
        "source_ids": [
          "A27-026"
        ],
        "anchor": "A27-026",
        "content_status": "active"
      },
      {
        "id": "R41-001",
        "kind": "h2",
        "text": "Сравнивать на собственной тишине и собственных словах",
        "source_ids": [
          "R41-001"
        ],
        "anchor": "R41-001",
        "content_status": "active"
      },
      {
        "id": "R41-002",
        "kind": "p",
        "text": "К Whisper добавлены GigaAM и Vosk как кандидаты, а не готовые победители. Грамматически ограниченное распознавание может быть удобно для малого набора команд; возможность менять словарь зависит от модели. Русский и английский проверяются отдельными наборами и смешанными названиями. Лицензия конкретных весов и доступный backend подтверждаются до включения в пакет. [163][164]",
        "source_ids": [
          "R41-002"
        ],
        "anchor": "R41-002",
        "content_status": "active"
      },
      {
        "id": "R41-003",
        "kind": "p",
        "text": "Негативный набор включает тишину, вентиляторы, телевизор, музыку, похожие слова, отрицания и оборванную фразу. Метрики различают ложный текст, ложную активацию и ложное физическое исполнение. Первые два не должны автоматически становиться третьим: нужны намерение, допустимый объект, текущие права и проверка команды.",
        "source_ids": [
          "R41-003"
        ],
        "anchor": "R41-003",
        "content_status": "active"
      },
      {
        "id": "R41-004",
        "kind": "p",
        "text": "Свободный LLM-парсер не входит в E0. Для E1 небольшой локальный кандидат может использовать constrained decoding, но синтаксически правильный JSON всё равно проходит совместную семантическую проверку. Бюджет вычисления измеряется на выбранном Hub; обещание «модель 1–4B всегда отвечает за 300 мс» не принимается без стенда.",
        "source_ids": [
          "R41-004"
        ],
        "anchor": "R41-004",
        "content_status": "active"
      }
    ],
    "origins": [
      "A27"
    ],
    "question": "Как отличить «включи свет» от «включи подсветку»?",
    "intro": "Распознавание речи возвращает текст, а не готовое разрешение на управление. Дальше нужно определить действие, найти объект в доступной комнате, проверить неоднозначность и полномочия.\n\n«Свет» и «освещение» могут быть синонимами одной роли. «Подсветка» может обозначать другой прибор или сразу несколько. Поэтому словарь связан с реестром комнаты, а уточняющий вопрос является нормальным результатом, не неудачей голосового интерфейса.",
    "id": "ch-41",
    "slug": "chapter-41"
  },
  {
    "number": 42,
    "title": "Микрофоны, динамик и шум куба",
    "group": "Акустика",
    "units": [
      {
        "kind": "lead",
        "text": "Акустическая система проектируется вместе с корпусом и вентиляцией. Для неё заранее предусматриваются размещение, объём и условия распространения звука.",
        "y": 168.675537109375,
        "page": 95,
        "old": 28,
        "id": "A28-001",
        "source_ids": [
          "A28-001"
        ],
        "anchor": "A28-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первый тракт — телефон или имеющийся Fifine. Он позволяет отделить качество модели от неизвестной геометрии микрофонов. Следующий шаг — выносной USB-массив с документированными режимами. Только после этого стоит переносить микрофоны в куб.",
        "y": 219.78143310546875,
        "page": 95,
        "old": 28,
        "id": "A28-002",
        "source_ids": [
          "A28-002"
        ],
        "anchor": "A28-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Один микрофон, массив или спутник",
        "y": 299.5755920410156,
        "page": 95,
        "old": 28,
        "id": "A28-003",
        "source_ids": [
          "A28-003"
        ],
        "anchor": "A28-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Один USB-микрофон прост в подключении и даёт понятный эталон. Массив может улучшить направленность и обработку, но требует правильной геометрии, синхронных каналов и подходящего алгоритма. Отдельный голосовой спутник позволяет расположить микрофоны ближе к человеку, оставив шумный вычислитель в стороне.",
        "y": 326.5932312011719,
        "page": 95,
        "old": 28,
        "id": "A28-004",
        "source_ids": [
          "A28-004"
        ],
        "anchor": "A28-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "reSpeaker — пример семейства готовых модулей, где важно выбрать точную модель и режимы USB. Нельзя покупать произвольную плату с таким названием и ожидать одинаковый набор каналов, AEC и firmware. [67]",
        "y": 398.9971618652344,
        "page": 95,
        "old": 28,
        "id": "A28-005",
        "source_ids": [
          "A28-005"
        ],
        "anchor": "A28-005",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Захват и формат",
        "y": 462.8163146972656,
        "page": 95,
        "old": 28,
        "id": "A28-006",
        "source_ids": [
          "A28-006"
        ],
        "anchor": "A28-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для многих speech-моделей удобен моно PCM 16 кГц, 16 бит: это 32 КБ/с без служебных данных. Десятисекундная фраза занимает около 320 КБ. Если устройство захватывает 48 кГц, выполняется одно качественное преобразование частоты, а не несколько каскадов в разных службах.",
        "y": 489.833984375,
        "page": 95,
        "old": 28,
        "id": "A28-007",
        "source_ids": [
          "A28-007"
        ],
        "anchor": "A28-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Параметры захвата фиксируются: частота, формат, каналы, gain, AGC, обработка устройства и программы. Иначе сравнение двух моделей может на самом деле сравнивать два разных аудиотракта. ALSA предоставляет низкоуровневый путь, а более высокий аудиосервер может упростить маршрутизацию; выбор проверяется по задержке и восстановлению USB. [40]",
        "y": 562.2379150390625,
        "page": 95,
        "old": 28,
        "id": "A28-008",
        "source_ids": [
          "A28-008"
        ],
        "anchor": "A28-008",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Эхо и опорный сигнал",
        "y": 56.314456939697266,
        "page": 96,
        "old": 28,
        "id": "A28-009",
        "source_ids": [
          "A28-009"
        ],
        "anchor": "A28-009",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A36",
        "caption": "AEC получает копию воспроизводимого потока. Без неё шумоподавление не становится полноценным эхокомпенсатором.",
        "y": 91.10079193115234,
        "page": 96,
        "old": 28,
        "id": "A28-010",
        "source_ids": [
          "A28-010"
        ],
        "anchor": "A28-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Когда станция отвечает через динамик, часть звука возвращается в микрофоны. Эхокомпенсации нужна синхронизированная опора того, что действительно воспроизводится. Разные устройства USB могут иметь разные часы и задержки; алгоритм должен учитывать дрейф, иначе компенсация ухудшается со временем.",
        "y": 463.9632568359375,
        "page": 96,
        "old": 28,
        "id": "A28-011",
        "source_ids": [
          "A28-011"
        ],
        "anchor": "A28-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Во время одновременной речи человека и динамика нужен режим double-talk. Простой запрет слушать во время ответа облегчает первую версию, но мешает перебиванию. Это допустимый явно описанный этап, а не скрытый дефект.",
        "y": 536.3671875,
        "page": 96,
        "old": 28,
        "id": "A28-012",
        "source_ids": [
          "A28-012"
        ],
        "anchor": "A28-012",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Механика микрофонов",
        "y": 600.1864624023438,
        "page": 96,
        "old": 28,
        "id": "A28-013",
        "source_ids": [
          "A28-013"
        ],
        "anchor": "A28-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Микрофонный модуль размещается вне прямого потока и подальше от крепления вентилятора. Отверстия должны соответствовать акустическому порту микрофона. Длинный узкий канал создаёт фильтрацию и резонансы; толстая декоративная ткань меняет частотную характеристику. Деталь сначала испытывается с открытым портом, затем с окончательной сеткой.",
        "y": 627.2040405273438,
        "page": 96,
        "old": 28,
        "id": "A28-014",
        "source_ids": [
          "A28-014"
        ],
        "anchor": "A28-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Динамик имеет собственную камеру или контролируемое акустическое оформление. Просто прикрутить его к общей стенке куба — значит передать вибрацию корпусу и микрофонам. Мощность динамика не компенсирует плохое расположение; для коротких ответов часто достаточно умеренной громкости и хорошей разборчивости.",
        "y": 715.5829467773438,
        "page": 96,
        "old": 28,
        "id": "A28-015",
        "source_ids": [
          "A28-015"
        ],
        "anchor": "A28-015",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Wake word",
        "y": 56.314456939697266,
        "page": 97,
        "old": 28,
        "id": "A28-016",
        "source_ids": [
          "A28-016"
        ],
        "anchor": "A28-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Постоянное ожидание слова активации — отдельный процесс с небольшим ресурсом. После него запускается основной тракт. openWakeWord предоставляет готовую инфраструктуру, но готовые модели и языковые возможности проверяются отдельно; русское слово «Кубик» не считается поддержанным только потому, что проект умеет wake word. [64]",
        "y": 83.33208465576172,
        "page": 97,
        "old": 28,
        "id": "A28-017",
        "source_ids": [
          "A28-017"
        ],
        "anchor": "A28-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "До обучения собственного слова активации первый куб использует кнопку push-to-talk. Затем собирается корпус положительных и отрицательных записей с согласием участников. Нельзя оценивать модель только по голосу автора, сказанному рядом с микрофоном.",
        "y": 171.71102905273438,
        "page": 97,
        "old": 28,
        "id": "A28-018",
        "source_ids": [
          "A28-018"
        ],
        "anchor": "A28-018",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Голос не является надёжной аутентификацией",
        "y": 251.50521850585938,
        "page": 97,
        "old": 28,
        "id": "A28-019",
        "source_ids": [
          "A28-019"
        ],
        "anchor": "A28-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Запись, динамик или похожий голос могут воспроизвести команду. Для света это один уровень риска, для замка или опасного оборудования — другой. Более чувствительные операции требуют подтверждения в приложении или физического действия. Распознавание говорящего, если оно появится, не должно автоматически считаться достаточной защитой.",
        "y": 278.5228576660156,
        "page": 97,
        "old": 28,
        "id": "A28-020",
        "source_ids": [
          "A28-020"
        ],
        "anchor": "A28-020",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Инженерная карта аудио",
        "y": 374.2920227050781,
        "page": 97,
        "old": 28,
        "id": "A28-021",
        "source_ids": [
          "A28-021"
        ],
        "anchor": "A28-021",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Испытание",
            "Что фиксировать",
            "Что считать проблемой"
          ],
          [
            "Тишина и обычный разговор",
            "Ложные активации",
            "Действие без обращения к системе"
          ],
          [
            "Вентиляторы под нагрузкой",
            "Ошибки команд и конец фразы",
            "Резкое ухудшение в рабочем режиме"
          ],
          [
            "Ответ динамика",
            "Эхо, повторная активация",
            "Станция командует сама себе"
          ],
          [
            "Отключение USB",
            "Восстановление устройства",
            "Нужна перезагрузка всего хаба"
          ],
          [
            "Дальняя речь",
            "Правильный объект и задержка",
            "Необоснованная уверенность при ошибке"
          ]
        ],
        "y": 403.41,
        "page": 97,
        "old": 28,
        "id": "A28-022",
        "source_ids": [
          "A28-022"
        ],
        "anchor": "A28-022",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Интеграция в корпус",
        "y": 629.0369873046875,
        "page": 97,
        "old": 28,
        "id": "A28-023",
        "source_ids": [
          "A28-023"
        ],
        "anchor": "A28-023",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сначала нужен рабочий голосовой тракт вне куба. Встроенная версия принимается только после сравнения с этим эталоном. До этого корпус резервирует место, питание и USB, но не фиксирует окончательное число микрофонов и отверстий.",
        "y": 647.4981079101562,
        "page": 97,
        "old": 28,
        "id": "A28-024",
        "source_ids": [
          "A28-024"
        ],
        "anchor": "A28-024",
        "content_status": "active"
      },
      {
        "id": "R42-001",
        "kind": "h2",
        "text": "Что акустическая обработка не защищает",
        "source_ids": [
          "R42-001"
        ],
        "anchor": "R42-001",
        "content_status": "active"
      },
      {
        "id": "R42-002",
        "kind": "p",
        "text": "AEC использует опорный сигнал собственного динамика и не обязан подавлять команды из телевизора или чужую запись. Для общего микрофона нужен отдельный ограниченный субъект «комната», не автоматическая идентификация владельца по голосу. Изменение доступа, SSH, перенос ключей и обновления голосом комнаты не разрешаются.",
        "source_ids": [
          "R42-002"
        ],
        "anchor": "R42-002",
        "content_status": "active"
      },
      {
        "id": "R42-003",
        "kind": "p",
        "text": "В аппаратном профиле микрофона предусматриваются физическое mute и индикатор, связанный с реальным отключением тракта, а не только с нарисованной иконкой. Точка отключения выбирается по модулю: питание MEMS, цифровой тракт или иной проверенный механизм. После mute тестируется отсутствие пригодного аудиопотока; внешний USB-микрофон требует своей документированной процедуры.",
        "source_ids": [
          "R42-003"
        ],
        "anchor": "R42-003",
        "content_status": "active"
      },
      {
        "id": "R42-004",
        "kind": "p",
        "text": "Для стенда речи задаём условия, а не универсальную цифру качества: расстояния 1 и 3 м, три оборотных режима вентилятора, измеренный шум, пять согласившихся дикторов при возможности. SNR, ERLE и AOP полезны как диагностические величины с определённой методикой. Их паспортное значение не заменяет долю правильных действий на RU/EN. Слово активации выбирается после сравнения нескольких кандидатов; короткое «Кубик» не считается уже утверждённым.",
        "source_ids": [
          "R42-004"
        ],
        "anchor": "R42-004",
        "content_status": "active"
      }
    ],
    "origins": [
      "A28"
    ],
    "question": "Как сделать так, чтобы куб слышал комнату, а не себя?",
    "intro": "Внутри станции работают вентиляторы, а рядом может играть собственный динамик. Микрофон воспринимает всё это вместе с речью. Размещение и механическая связь поэтому влияют на распознавание не меньше выбора модели.\n\nНачинаем с имеющегося выносного USB-микрофона и получаем опорный результат. Затем меняем только размещение или акустический узел и сравниваем на тех же фразах. Такой опыт показывает цену интеграции микрофона в корпус.",
    "id": "ch-42",
    "slug": "chapter-42"
  },
  {
    "number": 43,
    "title": "Обновление без потери управления",
    "group": "Эксплуатация",
    "units": [
      {
        "kind": "lead",
        "text": "Качество обновления проверяется при сбоях питания, связи и запуска. Успешная установка в штатных условиях составляет только часть этой проверки.",
        "y": 168.675537109375,
        "page": 100,
        "old": 30,
        "id": "A30-001",
        "source_ids": [
          "A30-001"
        ],
        "anchor": "A30-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В Koobique обновляются системный образ, Rust-приложения, прошивки MCU, модели речи и мобильное приложение. У них разные зависимости и способы отката. Один общий номер «версия системы 1.0» не описывает совместимость всех компонентов.",
        "y": 219.78143310546875,
        "page": 100,
        "old": 30,
        "id": "A30-002",
        "source_ids": [
          "A30-002"
        ],
        "anchor": "A30-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Воспроизводимая сборка",
        "y": 283.6005859375,
        "page": 100,
        "old": 30,
        "id": "A30-003",
        "source_ids": [
          "A30-003"
        ],
        "anchor": "A30-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для собственного кода фиксируются commit, toolchain, Cargo.lock, конфигурация функций и внешние SDK. Для моделей — источник, лицензия, хеш весов, квантование и runtime. Для плат — аппаратная ревизия и таблица контактов. Сборка, использующая latest из нескольких независимых проектов, плохо подходит для поиска регрессии.",
        "y": 310.61822509765625,
        "page": 100,
        "old": 30,
        "id": "A30-004",
        "source_ids": [
          "A30-004"
        ],
        "anchor": "A30-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Cargo vendor позволяет сохранить зависимости локально и подготовить более воспроизводимый офлайн-процесс. Однако этого недостаточно для всех системных библиотек и инструментов ESP-IDF: они также входят в манифест окружения. [94]",
        "y": 383.02215576171875,
        "page": 100,
        "old": 30,
        "id": "A30-005",
        "source_ids": [
          "A30-005"
        ],
        "anchor": "A30-005",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Обновление станции",
        "y": 446.8413391113281,
        "page": 100,
        "old": 30,
        "id": "A30-006",
        "source_ids": [
          "A30-006"
        ],
        "anchor": "A30-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первый разработческий стенд может обновлять приложение штатным пакетом с проверкой подписи и возможностью отката. Для самостоятельного appliance-профиля предпочтителен A/B-образ: новый раздел записывается отдельно, проверяется и загружается пробно. Выбранная инфраструктура должна учитывать загрузчик, данные и подтверждение успешной работы. [91]",
        "y": 473.85894775390625,
        "page": 100,
        "old": 30,
        "id": "A30-007",
        "source_ids": [
          "A30-007"
        ],
        "anchor": "A30-007",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A37",
        "caption": "A/B не решает миграции автоматически. Совместимость данных — обязательная часть отката.",
        "y": 56.69291687011719,
        "page": 101,
        "old": 30,
        "id": "A30-008",
        "source_ids": [
          "A30-008"
        ],
        "anchor": "A30-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Проверка здоровья включает не только факт запуска процесса: доступны конфигурация, БД, локальный API, сеть и критические системные ресурсы. Отсутствие одного спящего датчика не обязательно означает неудачный образ, поэтому health gate формулируется по классам зависимостей.",
        "y": 393.5626220703125,
        "page": 101,
        "old": 30,
        "id": "A30-009",
        "source_ids": [
          "A30-009"
        ],
        "anchor": "A30-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Обновление устройства",
        "y": 473.3567810058594,
        "page": 101,
        "old": 30,
        "id": "A30-010",
        "source_ids": [
          "A30-010"
        ],
        "anchor": "A30-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Устройство проверяет подпись, аппаратную совместимость и целевой раздел. При пробной загрузке выполняет самопроверку и подтверждает образ только после минимального функционирования. ESP-IDF описывает OTA и rollback; конкретная таблица разделов и правила принятия являются частью прошивки Koobique. [36]",
        "y": 500.37445068359375,
        "page": 101,
        "old": 30,
        "id": "A30-011",
        "source_ids": [
          "A30-011"
        ],
        "anchor": "A30-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Перед обновлением Actuator переводится в режим обслуживания. Для лампы E0 установлен OFF; после reset или OTA-reboot действует BOOT_INHIBIT до явного освобождения. Сохранение уровня при потере только Wi-Fi не распространяется на reboot. Для будущей опасной нагрузки нужен отдельный профиль. Во время обновления приложение показывает ограниченную доступность и не ставит обычные команды в бесконечную очередь.",
        "y": 572.7783813476562,
        "page": 101,
        "old": 30,
        "id": "A30-012",
        "source_ids": [
          "A30-012"
        ],
        "anchor": "A30-012",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Подпись, хеш и ротация",
        "y": 652.5725708007812,
        "page": 101,
        "old": 30,
        "id": "A30-013",
        "source_ids": [
          "A30-013"
        ],
        "anchor": "A30-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Хеш обнаруживает изменение относительно известного значения, но не отвечает, кто разрешил образ. Подпись и политика доверенных ключей решают авторизацию выпуска. Для метаданных, разделения ролей и ротации полезна модель TUF, но её внедрение должно учитывать автономную работу и восстановление доверенного времени. [84]",
        "y": 679.5902099609375,
        "page": 101,
        "old": 30,
        "id": "A30-014",
        "source_ids": [
          "A30-014"
        ],
        "anchor": "A30-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Откат после неудачного обновления и запрет возврата к уязвимой версии — разные требования. Нельзя включить anti-rollback так, что единственный рабочий старый образ навсегда заблокирован ещё до подтверждения нового. Нужна согласованная последовательность принятия.",
        "y": 57.42815399169922,
        "page": 102,
        "old": 30,
        "id": "A30-015",
        "source_ids": [
          "A30-015"
        ],
        "anchor": "A30-015",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Модели как исполняемые зависимости",
        "y": 137.22232055664062,
        "page": 102,
        "old": 30,
        "id": "A30-016",
        "source_ids": [
          "A30-016"
        ],
        "anchor": "A30-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Новая speech-модель может изменить смысл распознанной фразы без изменения Rust-core. Поэтому она проходит тот же корпус команд и негативных примеров. Хранится предыдущая принятая модель, а установка не удаляет её до проверки. Большой файл не должен заполнять диск так, что сломается журнал управления.",
        "y": 164.23995971679688,
        "page": 102,
        "old": 30,
        "id": "A30-017",
        "source_ids": [
          "A30-017"
        ],
        "anchor": "A30-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Формат модели также является входом сложного runtime. Используются доверенные источники, хеши и изоляция процесса. Модель не получает автоматически доступ к сети, ключам и файловой системе пользователя.",
        "y": 236.64389038085938,
        "page": 102,
        "old": 30,
        "id": "A30-018",
        "source_ids": [
          "A30-018"
        ],
        "anchor": "A30-018",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Локальная доставка",
        "y": 300.46307373046875,
        "page": 102,
        "old": 30,
        "id": "A30-019",
        "source_ids": [
          "A30-019"
        ],
        "anchor": "A30-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Обновление должно приниматься из локального файла через приложение или SSH. Интернет используется для удобного скачивания, но не для обязательной проверки лицензии перед запуском. Пакет содержит необходимые сведения о совместимости, чтобы станция могла отказать без обращения к внешнему серверу.",
        "y": 327.480712890625,
        "page": 102,
        "old": 30,
        "id": "A30-020",
        "source_ids": [
          "A30-020"
        ],
        "anchor": "A30-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Полный offline-набор для первого экземпляра включает образ ОС, toolchain или инструкцию его воспроизводимого получения, зависимости, прошивки, модели, мобильную сборку и документацию. Нельзя назвать систему полностью автономной для восстановления, если единственный загрузчик скачивается с недоступного сайта во время аварии.",
        "y": 399.8846740722656,
        "page": 102,
        "old": 30,
        "id": "A30-021",
        "source_ids": [
          "A30-021"
        ],
        "anchor": "A30-021",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Матрица обновления",
        "y": 504.53350830078125,
        "page": 102,
        "old": 30,
        "id": "A30-022",
        "source_ids": [
          "A30-022"
        ],
        "anchor": "A30-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Прервать загрузку файла, запись неактивного раздела, первую загрузку и подтверждение. Проверить неправильную подпись, не ту аппаратную ревизию и нехватку диска. Во всех случаях должен оставаться понятный путь к рабочей версии или локальному восстановлению.",
        "y": 522.99462890625,
        "page": 102,
        "old": 30,
        "id": "A30-023",
        "source_ids": [
          "A30-023"
        ],
        "anchor": "A30-023",
        "content_status": "active"
      },
      {
        "id": "R43-001",
        "kind": "h2",
        "text": "Два слота без rollback — ещё не отказоустойчивое OTA",
        "source_ids": [
          "R43-001"
        ],
        "anchor": "R43-001",
        "content_status": "active"
      },
      {
        "id": "R43-002",
        "kind": "p",
        "text": "В ESP-IDF требуется явно зафиксировать CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE и процедуру пробной загрузки. Приложение подтверждает пригодный образ через esp_ota_mark_app_valid_cancel_rollback только после self-test. Наличие ota_0 и ota_1 не включает механизм само по себе. В тест входят потеря питания при записи, невалидный образ, зависание пробной версии и возврат. [36]",
        "source_ids": [
          "R43-002"
        ],
        "anchor": "R43-002",
        "content_status": "active"
      },
      {
        "id": "R43-003",
        "kind": "p",
        "text": "Self-test E0: корректная привязка и чтение конфигурации, безопасный выход OFF, работа кнопки, запуск критических задач и совместимость данных. Интернет не требуется; бесконечное ожидание роутера не должно сделать каждое обновление неисправным. До reboot Actuator согласованно переводится в обслуживание; длительность недоступности кнопки показывается и измеряется.",
        "source_ids": [
          "R43-003"
        ],
        "anchor": "R43-003",
        "content_status": "active"
      },
      {
        "id": "R43-004",
        "kind": "p",
        "text": "Rollback и anti-rollback согласуются до записи необратимых параметров. Нельзя повысить нижнюю разрешённую security version так, что предыдущий единственный исправный образ перестанет загружаться до подтверждения нового. Данные владения, fencing и отзыва не должны возвращаться вместе с A/B-разделом. Если старая прошивка не умеет читать новую схему доверия, её возврат не объявляется пригодным.",
        "source_ids": [
          "R43-004"
        ],
        "anchor": "R43-004",
        "content_status": "active"
      },
      {
        "id": "R43-005",
        "kind": "p",
        "text": "Для автономного Hub выбран кандидат RAUC с интеграцией конкретного загрузчика, boot counting, health gate и отдельным /data. На H0 пока достаточно версионированного пакета приложения и отката в контролируемой среде; это не имитация готового A/B всей ОС. Сравнение SWUpdate/OSTree остаётся альтернативой, если выбранная плата не укладывается в исходный путь. [171]",
        "source_ids": [
          "R43-005"
        ],
        "anchor": "R43-005",
        "content_status": "active"
      },
      {
        "id": "R43-006",
        "kind": "p",
        "text": "Новая версия работает только с проверенной матрицей Core × MCU × app × schema × wire. Совместимость N/N−1 — цель испытаний, не обещание по номеру. Core N и N−1 могут оба поддерживать KQIP 0.2; старый учебный Hello0.1 не обязан поддерживаться. Обновление идёт через один узел-кандидат, наблюдение и затем остальные. Чанки bulk имеют offset, длину и контроль целого подписанного образа; resume относится к тому же хешу. При будущей несовместимой смене wire нужны negotiation/dual-profile стратегия и проверка поэтапного обновления без ослабления доверия.",
        "source_ids": [
          "R43-006"
        ],
        "anchor": "R43-006",
        "content_status": "active"
      },
      {
        "id": "R43-007",
        "kind": "p",
        "text": "В CI фиксируются toolchain/lockfiles/SDK, проверяются cargo-audit, cargo-deny, происхождение пакетов и SBOM. Положительный результат этих инструментов означает отсутствие найденных известных проблем по их базе, а не доказательство отсутствия уязвимостей. Изменение runtime модели также проходит проверку поведения. [172][173]",
        "source_ids": [
          "R43-007"
        ],
        "anchor": "R43-007",
        "content_status": "active"
      }
    ],
    "origins": [
      "A30"
    ],
    "question": "Как изменять систему, не теряя её работоспособность?",
    "intro": "Обновление меняет не один файл. Вместе могут измениться код, формат данных, модель, драйвер и правила интерпретации команды. Возможность загрузить прежний раздел ещё не гарантирует, что прежняя программа прочитает уже изменённую базу.\n\nПоэтому у обновления есть проверяемая совместимость и план возврата. Локальная доставка остаётся доступной без интернета. Для первого разработческого стенда и для готовой станции процедуры могут различаться, но обе должны оставлять понятное восстановление.",
    "id": "ch-43",
    "slug": "chapter-43"
  },
  {
    "number": 44,
    "title": "Один день в кабинете",
    "group": "Сквозной пример",
    "units": [
      {
        "kind": "lead",
        "text": "Ниже задан конкретный световой сценарий и его проектный профиль для первых испытаний. Пороговые значения подлежат проверке в выбранной комнате и не объявляются универсальными настройками комфорта.",
        "y": 168.675537109375,
        "page": 126,
        "old": 38,
        "id": "A38-001",
        "source_ids": [
          "A38-001"
        ],
        "anchor": "A38-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Условия опыта",
        "y": 236.06771850585938,
        "page": 126,
        "old": 38,
        "id": "A38-002",
        "source_ids": [
          "A38-002"
        ],
        "anchor": "A38-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В комнате установлены Sense, Presence и Actuator. Лампа 12 В изображает основной свет. Телефон имеет роль обычного пользователя. Температура пока только измеряется и объясняется; никакой обогреватель не включается автоматически. Все задержки и пороги ниже — стартовые параметры стенда, которые корректируются после наблюдений.",
        "y": 263.0853576660156,
        "page": 126,
        "old": 38,
        "id": "A38-003",
        "source_ids": [
          "A38-003"
        ],
        "anchor": "A38-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Sense выдаёт освещённость раз в секунду и температуру раз в несколько секунд. Presence публикует признаки движения и состояние доступности. MCU знает момент своего измерения; сервер знает время приёма и предел актуальности. Пропущенный пакет не превращается автоматически в отсутствие человека.",
        "y": 351.46429443359375,
        "page": 126,
        "old": 38,
        "id": "A38-004",
        "source_ids": [
          "A38-004"
        ],
        "anchor": "A38-004",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Параметр профиля",
            "Начальная гипотеза",
            "Зачем он нужен"
          ],
          [
            "Темно для входа",
            "Ниже 120 лк",
            "Только условие включения при входе"
          ],
          [
            "Подтверждение входа",
            "Несколько согласованных признаков за 1–2 с",
            "Ограничить одиночные ложные срабатывания"
          ],
          [
            "Ожидание ухода",
            "120 с только Presence-qualified",
            "В PIR-only автоматический OFF запрещён"
          ],
          [
            "Ручной режим E0",
            "До явного освобождения",
            "Повторная темнота не отменяет OFF"
          ],
          [
            "Срок живого lux",
            "Несколько ожидаемых интервалов измерения",
            "Не использовать вчерашнюю темноту"
          ],
          [
            "TTL команды включения",
            "2 с для тестового профиля",
            "Не включить свет сильно позже события"
          ]
        ],
        "y": 425.97,
        "page": 126,
        "old": 38,
        "id": "A38-005",
        "source_ids": [
          "A38-005"
        ],
        "anchor": "A38-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Эти числа не являются нормами освещённости рабочего места или гарантиями распознавания присутствия. Истинная освещённость оценивается в нужной плоскости, а оптимальное ожидание ухода зависит от поведения человека и датчика. Для неподвижной работы один PIR может принципиально не обеспечивать требуемый результат.",
        "y": 706.2723388671875,
        "page": 126,
        "old": 38,
        "id": "A38-006",
        "source_ids": [
          "A38-006"
        ],
        "anchor": "A38-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Хронология",
        "y": 56.314456939697266,
        "page": 127,
        "old": 38,
        "id": "A38-007",
        "source_ids": [
          "A38-007"
        ],
        "anchor": "A38-007",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A41",
        "caption": "Проектный пример замкнутого сценария. Переходы через EMPTY относятся к отдельно допущенному Presence-qualified профилю E1; E0 с PIR-only не подтверждает отсутствие человека и не выполняет автоматический OFF по молчанию датчика. Времена иллюстративны: они показывают порядок и причины, а не измеренные задержки системы.",
        "y": 91.10079193115234,
        "page": 127,
        "old": 38,
        "id": "A38-008",
        "source_ids": [
          "A38-008"
        ],
        "anchor": "A38-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "До входа. Последний достоверный lux равен 42, присутствие Empty, реле OFF. Это не одно «состояние комнаты», а согласованный набор наблюдений с собственной свежестью.",
        "y": 509.2073974609375,
        "page": 127,
        "old": 38,
        "id": "A38-009",
        "source_ids": [
          "A38-009"
        ],
        "anchor": "A38-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Человек входит. Новый признак запускает Candidate. Затем подтверждается Occupied. Правило проверяет актуальность lux, отсутствие ручного запрета и права автоматизации на основной свет. Оно создаёт команду set(true) с отдельным ID и причиной: «вход в тёмную комнату».",
        "y": 565.6362915039062,
        "page": 127,
        "old": 38,
        "id": "A38-010",
        "source_ids": [
          "A38-010"
        ],
        "anchor": "A38-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Станция принимает команду. Журнал и outbox фиксируются транзакционно. Приложение показывает «выполняется», а не рисует безусловный успех. Устройство сверяет срок, полномочия контроллера и политику выхода, включает драйвер и сообщает новую версию состояния.",
        "y": 638.0402221679688,
        "page": 127,
        "old": 38,
        "id": "A38-011",
        "source_ids": [
          "A38-011"
        ],
        "anchor": "A38-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Lux растёт. Например, до 420 лк. Правило не выключает лампу только потому, что стало светло: это ожидаемый эффект собственного действия. Подтверждение можно усилить измерением тока, но рост света от солнца не является надёжным доказательством исправности именно лампы.",
        "y": 710.4441528320312,
        "page": 127,
        "old": 38,
        "id": "A38-012",
        "source_ids": [
          "A38-012"
        ],
        "anchor": "A38-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Человек вручную выключает свет. Появляется ручное переопределение. Пока оно действует, повторная темнота не должна немедленно включать лампу. Смена владельца состояния видна в журнале и интерфейсе.",
        "y": 57.42815399169922,
        "page": 128,
        "old": 38,
        "id": "A38-013",
        "source_ids": [
          "A38-013"
        ],
        "anchor": "A38-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В профиле Presence-qualified человек уходит, и VACATING ждёт проверяемое время с учётом новых признаков. Выключение допустимо только после свидетельства ухода и при отсутствии ручного запрета. В PIR-only этого перехода к автоматическому OFF нет. При потере обязательного датчика получается UNKNOWN с уведомлением; отключение по предельному таймеру возможно лишь как заранее согласованная отдельная политика.",
        "y": 113.85709381103516,
        "page": 128,
        "old": 38,
        "id": "A38-014",
        "source_ids": [
          "A38-014"
        ],
        "anchor": "A38-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Формальная таблица решения",
        "y": 209.62625122070312,
        "page": 128,
        "old": 38,
        "id": "A38-015",
        "source_ids": [
          "A38-015"
        ],
        "anchor": "A38-015",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Ситуация",
            "Действие",
            "Почему"
          ],
          [
            "Occupied + свежо и темно + нет override",
            "Включить основной свет",
            "Выполнено условие входа"
          ],
          [
            "Occupied + светло после собственного ON",
            "Сохранить состояние",
            "Не создавать обратное переключение"
          ],
          [
            "Occupied + ручной OFF",
            "Не включать автоматически",
            "У человека временный приоритет"
          ],
          [
            "Vacating + новый признак",
            "Вернуться в Occupied",
            "Уход не подтверждён"
          ],
          [
            "Датчик недоступен",
            "Unknown, выбранный режим деградации",
            "Нет данных — не доказательство отсутствия"
          ],
          [
            "Просроченная команда из очереди",
            "Отклонить",
            "Ситуация могла измениться"
          ],
          [
            "Два светильника с одним alias",
            "Уточнить объект",
            "Название неоднозначно"
          ]
        ],
        "y": 238.74,
        "page": 128,
        "old": 38,
        "id": "A38-016",
        "source_ids": [
          "A38-016"
        ],
        "anchor": "A38-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Решение лучше реализовать как чистую функцию входного состояния и политики, которая выдаёт намерение и объяснение. Исполнение, запись на диск и сеть выполняются отдельно. Так один и тот же журнал можно воспроизвести в симуляторе и убедиться, что новая версия правил не изменила поведение неожиданно.",
        "y": 545.5593872070312,
        "page": 128,
        "old": 38,
        "id": "A38-017",
        "source_ids": [
          "A38-017"
        ],
        "anchor": "A38-017",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Две комнаты и два светильника",
        "y": 625.3535766601562,
        "page": 128,
        "old": 38,
        "id": "A38-018",
        "source_ids": [
          "A38-018"
        ],
        "anchor": "A38-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "При добавлении коридора устройства не получают имена, которые одновременно служат постоянными ID. Физический device_id остаётся неизменным; расположение, человеческие названия и группы меняются отдельно. Основной свет кабинета и подсветка монитора получают разные capabilities и разные alias-наборы.",
        "y": 652.3711547851562,
        "page": 128,
        "old": 38,
        "id": "A38-019",
        "source_ids": [
          "A38-019"
        ],
        "anchor": "A38-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Команда «включи освещение» в кабинете разрешается в основной свет, если это явно задано профилем. «Включи подсветку» разрешается только при одном подходящем объекте в доступном контексте. Если их два, приложение или голос задаёт вопрос. Контекст телефона нельзя считать достоверным положением человека, когда он получен лишь из последнего открытого экрана.",
        "y": 57.42815399169922,
        "page": 129,
        "old": 38,
        "id": "A38-020",
        "source_ids": [
          "A38-020"
        ],
        "anchor": "A38-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Групповая сцена не атомарна физически. Если одна лампа недоступна, результат содержит успех первой и неопределённость второй. Компенсирующее выключение первой не всегда желаемо: стратегия частичного исполнения задаётся самой сценой.",
        "y": 145.80709838867188,
        "page": 129,
        "old": 38,
        "id": "A38-021",
        "source_ids": [
          "A38-021"
        ],
        "anchor": "A38-021",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Проверка температуры как процесса",
        "y": 209.62625122070312,
        "page": 129,
        "old": 38,
        "id": "A38-022",
        "source_ids": [
          "A38-022"
        ],
        "anchor": "A38-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Даже без отопительного исполнительного узла можно изучать динамику: открыть дверь, включить вычислительную нагрузку, переместить датчик и сравнить изменения. При этом нельзя смешивать эксперимент по нагреву комнаты с экспериментом по самонагреву измерителя.",
        "y": 236.64389038085938,
        "page": 129,
        "old": 38,
        "id": "A38-023",
        "source_ids": [
          "A38-023"
        ],
        "anchor": "A38-023",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для оценки постоянной времени сначала стабилизируют условия, затем выполняют одно документированное изменение. Модель сопоставляется с отложенным участком данных, а не только с тем, на котором подобраны параметры. Открытое окно, солнце и человек — внешние воздействия; без их учёта точная подгонка ещё не доказывает физическую адекватность.",
        "y": 309.0478515625,
        "page": 129,
        "old": 38,
        "id": "A38-024",
        "source_ids": [
          "A38-024"
        ],
        "anchor": "A38-024",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Небольшой вентилятор внутри комнаты главным образом перемешивает воздух. Он не считается управляемой вентиляцией наружным воздухом. Будущая функция «проветрить» потребует отдельного физического исполнительного механизма и ограничений, а не переименования обычного вентилятора.",
        "y": 397.4267578125,
        "page": 129,
        "old": 38,
        "id": "A38-025",
        "source_ids": [
          "A38-025"
        ],
        "anchor": "A38-025",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Набор испытательных историй",
        "y": 486.1006164550781,
        "page": 129,
        "old": 38,
        "id": "A38-026",
        "source_ids": [
          "A38-026"
        ],
        "anchor": "A38-026",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Записать вход в темноте; вход при дневном свете; неподвижную работу; ручной OFF; уход и быстрый возврат; пропавший датчик; поздний пакет; смену имени светильника; частичный отказ сцены. Прогнать каждую историю через старую и новую версию правил. Расхождение должно объясняться конкретным изменением политики.",
        "y": 504.5617980957031,
        "page": 129,
        "old": 38,
        "id": "A38-027",
        "source_ids": [
          "A38-027"
        ],
        "anchor": "A38-027",
        "content_status": "active"
      },
      {
        "kind": "lead",
        "text": "Разобранный световой сценарий показывает, как собственное воздействие лампы меняет вход датчика. Модель этого воздействия позволяет проверить поведение контура до добавления нейросети.",
        "id": "I16-001",
        "old": "I16",
        "source_ids": [
          "I16-001"
        ],
        "anchor": "I16-001",
        "content_status": "active"
      },
      {
        "id": "N-030",
        "kind": "h2",
        "text": "Два примера — два разных договора",
        "new": true,
        "source_ids": [
          "N-030"
        ],
        "anchor": "N-030",
        "content_status": "active"
      },
      {
        "id": "N-031",
        "kind": "p",
        "text": "Для E0 используются вход в темноту ниже условных 120 лк, ручной режим до явного освобождения и срок нового исполнения до 2 с при пригодной синхронизации. Подтверждение входа 1–2 с и выдержка ухода 120 с — отдельный профиль Presence-qualified. Временный ручной режим 30 минут остаётся возможностью E1 и не является настройкой E0. Числа — цели учебного опыта, не норматив освещения.",
        "new": true,
        "source_ids": [
          "N-031"
        ],
        "anchor": "N-031",
        "content_status": "active"
      },
      {
        "id": "N-032",
        "kind": "p",
        "text": "Следующий опыт специально использует другие значения: внешний свет 150 лк, вклад лампы около 420 лк, пороги 280/340 лк и минимальную паузу 30 с. Он нужен, чтобы увидеть ошибку обратной связи и сравнить два алгоритма. Его 600 секунд существуют в симуляторе, а не в журнале испытанной станции.",
        "new": true,
        "source_ids": [
          "N-032"
        ],
        "anchor": "N-032",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Состав и условия опыта",
        "id": "I16-002",
        "old": "I16",
        "source_ids": [
          "I16-002"
        ],
        "anchor": "I16-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сохраняем первый комплект: вынесенный сенсор света и температуры, узел присутствия, реле с безопасной низковольтной лампой, станция и KMP-приложение. Никаких новых аппаратных закупок ради AI на этом этапе не требуется; исследование можно выполнять на имеющемся ПК.",
        "id": "I16-003",
        "old": "I16",
        "source_ids": [
          "I16-003"
        ],
        "anchor": "I16-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Пользователь связывает реле с основным светом, а не с абстрактной «розеткой». Приложение показывает размещение датчика и назначение рабочей зоны. Ручная кнопка имеет приоритет. Автоматическая настройка вклада лампы разрешена только в отдельном режиме эксперимента.",
        "id": "I16-004",
        "old": "I16",
        "source_ids": [
          "I16-004"
        ],
        "anchor": "I16-004",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Диагностический опыт: лампа влияет на свой датчик",
        "id": "I16-005",
        "old": "I16",
        "edited": true,
        "source_ids": [
          "I16-005"
        ],
        "anchor": "I16-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В комнате 150 лк от внешнего света; лампа добавляет примерно 420 лк в измеряемой точке. Учебное правило включает ниже 280 лк и выключает выше 340 лк. После включения датчик видит около 570 лк, правило выключает свет, затем снова видит 150 лк. Гистерезис сам по себе не устраняет эту обратную связь. Минимальная пауза лишь замедляет повторение.",
        "id": "I16-006",
        "old": "I16",
        "source_ids": [
          "I16-006"
        ],
        "anchor": "I16-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Правильная модель различает фон и вклад собственного действия:",
        "id": "I16-007",
        "old": "I16",
        "source_ids": [
          "I16-007"
        ],
        "anchor": "I16-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "KQMATH040TOKEN",
        "id": "I16-008",
        "old": "I16",
        "source_ids": [
          "I16-008"
        ],
        "anchor": "I16-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "KQMATH041TOKEN — фактически подтверждённое состояние лампы, а не только желание сервера. Неизвестный выход делает вычитание ненадёжным; оценщик должен это отразить.",
        "id": "I16-009",
        "old": "I16",
        "source_ids": [
          "I16-009"
        ],
        "anchor": "I16-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Воспроизводимая симуляция",
        "id": "I16-010",
        "old": "I16",
        "source_ids": [
          "I16-010"
        ],
        "anchor": "I16-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В архиве находится simulation/room_replay.py. Это исследовательский скрипт на стандартной библиотеке Python, не часть реализации Koobique. Он моделирует 600 секунд, постоянное присутствие, растущий фон с одним затемнением, шум и две политики. Минимальная пауза между переключениями — 30 с в обоих случаях. Измерение предшествует команде; её эффект появляется в следующем отсчёте.",
        "id": "I16-011",
        "old": "I16",
        "source_ids": [
          "I16-011"
        ],
        "anchor": "I16-011",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Показатель",
            "По сырому свету",
            "С моделью лампы"
          ],
          [
            "Число переключений",
            "12",
            "2"
          ],
          [
            "Секунды ниже 280 лк",
            "167",
            "1"
          ],
          [
            "Время включения, с",
            "180",
            "446"
          ]
        ],
        "id": "I16-012",
        "old": "I16",
        "source_ids": [
          "I16-012"
        ],
        "anchor": "I16-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Это результаты синтетического опыта при заданной модели, а не обещанные показатели оборудования. Вторая политика держит лампу дольше и расходует больше энергии в этой модели. Она лучше выполняет заданную световую цель, но не «побеждает по всем метрикам».",
        "id": "I16-013",
        "old": "I16",
        "source_ids": [
          "I16-013"
        ],
        "anchor": "I16-013",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I16",
        "caption": "Синтетическое состояние лампы на одном внешнем сценарии. Верхняя линия — пороги по сырому свету, нижняя — учёт своего вклада. Все параметры, CSV и начальное зерно генератора доступны в архиве.",
        "id": "I16-014",
        "old": "I16",
        "source_ids": [
          "I16-014"
        ],
        "anchor": "I16-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Что происходит при ошибке модели",
        "id": "I16-015",
        "old": "I16",
        "source_ids": [
          "I16-015"
        ],
        "anchor": "I16-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Скрипт также меняет оценку вклада лампы. При истинных 420 лк и предположении 320 лк результат ухудшается: 4 переключения и 44 секунды ниже цели. При предположении 470 лк лампа остаётся включённой 599 секунд. Это показывает необходимость проверки коэффициента и учёта неопределённости, а не превосходство любой «модельной» логики.",
        "id": "I16-016",
        "old": "I16",
        "source_ids": [
          "I16-016"
        ],
        "anchor": "I16-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Разумный следующий эталон — автомат с моделью фона, проверкой достоверности и ограничениями. Именно с ним следует сравнивать нейросетевую поправку. Простой сломанный порог нужен здесь для объяснения механизма, а не для рекламного доказательства AI.",
        "id": "I16-017",
        "old": "I16",
        "source_ids": [
          "I16-017"
        ],
        "anchor": "I16-017",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Реальная последовательность проверки",
        "id": "I16-018",
        "old": "I16",
        "source_ids": [
          "I16-018"
        ],
        "anchor": "I16-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сначала выполняем опыт только в записи и симуляции. Затем вручную включаем низковольтную лампу и проверяем знак и устойчивость изменения света. После этого разрешаем ограниченный автомат с кнопкой отмены. Отдельно проверяем затенение датчика рукой, выключение питания узла, обрыв лампы и потерю подтверждения.",
        "id": "I16-019",
        "old": "I16",
        "source_ids": [
          "I16-019"
        ],
        "anchor": "I16-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Состояния реле и света не должны сливаться в один зелёный индикатор. Возможны: «команда принята», «выход ON», «световой эффект не подтверждён», «цель достигнута». Если датчик закрыт, повторный ON не является лечением.",
        "id": "I16-020",
        "old": "I16",
        "source_ids": [
          "I16-020"
        ],
        "anchor": "I16-020",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Присутствие и ручной режим",
        "id": "I16-021",
        "old": "I16",
        "source_ids": [
          "I16-021"
        ],
        "anchor": "I16-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Во время неподвижного чтения отсутствие PIR-событий не должно само по себе запускать выключение. Потеря радара переводит оценку к недостаточным данным. Для нашей учебной лампы согласуем удержание освещения и уведомление; для другого класса нагрузки политика может быть иной.",
        "id": "I16-022",
        "old": "I16",
        "source_ids": [
          "I16-022"
        ],
        "anchor": "I16-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "После ручного OFF пакет освещения получает временное или постоянное ограничение согласно выбору пользователя. Модель предпочтений может предложить изменить профиль, но не снимает ручной запрет автоматически. «Человек часто выключает эту лампу» — основание для вопроса, не для переписывания прав.",
        "id": "I16-023",
        "old": "I16",
        "source_ids": [
          "I16-023"
        ],
        "anchor": "I16-023",
        "content_status": "active"
      },
      {
        "id": "R44-001",
        "kind": "h2",
        "text": "Контрольная история ручного OFF",
        "source_ids": [
          "R44-001"
        ],
        "anchor": "R44-001",
        "content_status": "active"
      },
      {
        "id": "R44-002",
        "kind": "p",
        "text": "Состояние до действия: boot B, output_revision 38, manual_revision 7. A с execution_seq 19 включает лампу; ответ теряется. Кнопка выключает её, увеличивает ревизии и включает MANUAL_HOLD. Повтор A возвращает её прошлый результат без GPIO. Новый автоматический B даже с актуальными числами отвергается ручным режимом. Только явное освобождение разрешает пересчитать решение. Именно эта история проверяет пользовательское обещание, которое не покрывается одним сравнением версии.",
        "source_ids": [
          "R44-002"
        ],
        "anchor": "R44-002",
        "content_status": "active"
      },
      {
        "id": "R44-003",
        "kind": "p",
        "text": "Числа симуляции 12 → 2 переключения и 167 → 1 секунды дефицита сохранены как учебный опыт. Время ON выросло с 180 до 446 с, примерно в 2,48 раза. При постоянной мощности это рост энергии лампы, а не доказанная экономия. Следующее сравнение проводится против разумного автомата на одинаковых независимых эпизодах, с учётом энергии Hub, ручных исправлений и неопределённости коэффициента.",
        "source_ids": [
          "R44-003"
        ],
        "anchor": "R44-003",
        "content_status": "active"
      }
    ],
    "origins": [
      "A38",
      "I16"
    ],
    "question": "Как все слои ведут себя в одной истории?",
    "intro": "Теперь соберём систему обратно. Сначала пройдём рабочий профиль кабинета: вход, автоматический свет, ручная команда, обрыв связи и возвращение устройства. Затем отдельно проведём диагностический опыт, специально устроенный так, чтобы выявить влияние собственной лампы.\n\nУ этих двух примеров разные пороги и цели. Первый задаёт поведение комнаты; второй сравнивает алгоритмы в симуляторе. Их числа нельзя перемешивать и выдавать за единственную рекомендованную настройку реального освещения.",
    "id": "ch-44",
    "slug": "chapter-44"
  },
  {
    "number": 45,
    "title": "За пределами одной комнаты",
    "group": "Развитие",
    "units": [
      {
        "kind": "lead",
        "text": "Расширение проверяет границы модели. Оно не должно автоматически усложнять первый кабинет.",
        "y": 168.675537109375,
        "page": 107,
        "old": 32,
        "id": "A32-001",
        "source_ids": [
          "A32-001"
        ],
        "anchor": "A32-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Koobique проектируется так, чтобы новый физический процесс добавлял собственные наблюдения, модель и ограничения, а не требовал переписывать приложение и протокол. Но универсальность не означает одинаковый MCU, транспорт или режим отказа для всего.",
        "y": 219.78143310546875,
        "page": 107,
        "old": 32,
        "id": "A32-002",
        "source_ids": [
          "A32-002"
        ],
        "anchor": "A32-002",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A40",
        "caption": "Варианты развития одной платформы. Каждый сценарий имеет собственные риски и критерии допуска.",
        "y": 299.9540710449219,
        "page": 107,
        "old": 32,
        "id": "A32-003",
        "source_ids": [
          "A32-003"
        ],
        "anchor": "A32-003",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Кабинет: несколько световых групп",
        "y": 666.5532836914062,
        "page": 107,
        "old": 32,
        "id": "A32-004",
        "source_ids": [
          "A32-004"
        ],
        "anchor": "A32-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Основной свет, подсветка монитора и рабочая лампа имеют разные имена и роли. Сцена «работа» может включать два канала, но её результат бывает частичным. Приложение показывает, какой канал не подтвердился, а не скрывает это за общим успехом.",
        "y": 693.5708618164062,
        "page": 107,
        "old": 32,
        "id": "A32-005",
        "source_ids": [
          "A32-005"
        ],
        "anchor": "A32-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Диммирование добавляет диапазон, нелинейность яркости и возможное мерцание. Механическое реле не подходит для PWM. Для низковольтной ленты используется рассчитанный полупроводниковый ключ или готовый драйвер; световой коэффициент измеряется по уровням, а не считается линейным по умолчанию.",
        "y": 57.42815399169922,
        "page": 108,
        "old": 32,
        "id": "A32-006",
        "source_ids": [
          "A32-006"
        ],
        "anchor": "A32-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Коридор и ночной режим",
        "y": 137.22232055664062,
        "page": 108,
        "old": 32,
        "id": "A32-007",
        "source_ids": [
          "A32-007"
        ],
        "anchor": "A32-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Здесь важнее быстрое включение и предсказуемый путь человека. Длительный анализ присутствия может быть лишним. Ночной профиль ограничивает яркость, а ручной режим доступен физически. Выключение при потере датчика не должно внезапно оставлять проход в темноте.",
        "y": 164.23995971679688,
        "page": 108,
        "old": 32,
        "id": "A32-008",
        "source_ids": [
          "A32-008"
        ],
        "anchor": "A32-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Такой сценарий показывает, почему общими являются механизм policy и capability, но не конкретные таймеры. Модель кабинета с длительным сидением нельзя без изменений перенести на проходную зону.",
        "y": 236.64389038085938,
        "page": 108,
        "old": 32,
        "id": "A32-009",
        "source_ids": [
          "A32-009"
        ],
        "anchor": "A32-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Мастерская и измерение нагрузки",
        "y": 300.46307373046875,
        "page": 108,
        "old": 32,
        "id": "A32-010",
        "source_ids": [
          "A32-010"
        ],
        "anchor": "A32-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Можно наблюдать ток, температуру корпуса оборудования и состояние розетки через безопасные готовые измерительные устройства. Но автоматический запуск инструмента — новый класс риска. Локальная аварийная остановка и аппаратные блокировки не заменяются приложением или speech-моделью.",
        "y": 327.480712890625,
        "page": 108,
        "old": 32,
        "id": "A32-011",
        "source_ids": [
          "A32-011"
        ],
        "anchor": "A32-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первого исследования достаточно низковольтной эквивалентной нагрузки. Результаты по протоколу и хранению переносятся, а допуск к реальному оборудованию требует отдельного электрического и функционального анализа.",
        "y": 399.8846740722656,
        "page": 108,
        "old": 32,
        "id": "A32-012",
        "source_ids": [
          "A32-012"
        ],
        "anchor": "A32-012",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "CO₂ и вентиляция",
        "y": 463.7038269042969,
        "page": 108,
        "old": 32,
        "id": "A32-013",
        "source_ids": [
          "A32-013"
        ],
        "anchor": "A32-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Добавление SCD41 или другого документированного CO₂-сенсора расширяет модель микроклимата. Он измеряет другую физическую величину, чем температура или условный VOC-индекс. Поддержка калибровки, режимов и размещения изучается отдельно; приложение не должно переименовывать разные показатели в один абстрактный «качество воздуха». [96]",
        "y": 490.7214660644531,
        "page": 108,
        "old": 32,
        "id": "A32-014",
        "source_ids": [
          "A32-014"
        ],
        "anchor": "A32-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Вентиляционное управление требует знать, что именно делает исполнитель: перемешивает воздух внутри или обеспечивает воздухообмен. Вентилятор на столе не уменьшает CO₂ так же, как приточно-вытяжная система. Это пример полезного физического понимания, которое должно быть явным в модели.",
        "y": 579.1004028320312,
        "page": 108,
        "old": 32,
        "id": "A32-015",
        "source_ids": [
          "A32-015"
        ],
        "anchor": "A32-015",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Теплица, протечка и полив",
        "y": 658.8945922851562,
        "page": 108,
        "old": 32,
        "id": "A32-016",
        "source_ids": [
          "A32-016"
        ],
        "anchor": "A32-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Влага, насос и клапан вводят ограничение максимального времени, проверку расхода и режим безопасного отключения. Старое сообщение «полить» не может исполняться после возвращения сети без срока действия. Влажность грунта зависит от типа сенсора, почвы и установки; голый ADC не является универсальным процентом воды.",
        "y": 685.9122314453125,
        "page": 108,
        "old": 32,
        "id": "A32-017",
        "source_ids": [
          "A32-017"
        ],
        "anchor": "A32-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "До реального полива модель проверяется на безопасном стенде. Клапан с нормальным состоянием, резервирование и обнаружение протечки выбираются по последствиям отказа, а не по тем же правилам, что лампа.",
        "y": 57.42815399169922,
        "page": 109,
        "old": 32,
        "id": "A32-018",
        "source_ids": [
          "A32-018"
        ],
        "anchor": "A32-018",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Батарейный и носимый узел",
        "y": 121.24732971191406,
        "page": 109,
        "old": 32,
        "id": "A32-019",
        "source_ids": [
          "A32-019"
        ],
        "anchor": "A32-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для носимого устройства BLE может быть рациональнее Wi-Fi. Узел хранит события локально, использует boot_id и sequence, передаёт историю пакетами и не исполняет старые действия как текущие. Телефон становится возможным шлюзом вне дома, но его фоновые ограничения учитываются.",
        "y": 148.26495361328125,
        "page": 109,
        "old": 32,
        "id": "A32-020",
        "source_ids": [
          "A32-020"
        ],
        "anchor": "A32-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Энергетический бюджет считается по состояниям. При условных 20 мА в течение 2 секунд каждые 60 секунд и 0,02 мА в остальное время средний ток около 0,69 мА. Идеальный ресурс аккумулятора 500 мА·ч — около 30 суток, но потери преобразования, саморазряд, пики и полезная ёмкость уменьшают результат. Это расчётная иллюстрация, не прогноз для ESP32-DevKit.",
        "y": 220.66888427734375,
        "page": 109,
        "old": 32,
        "id": "A32-021",
        "source_ids": [
          "A32-021"
        ],
        "anchor": "A32-021",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Проводные устройства",
        "y": 316.4380798339844,
        "page": 109,
        "old": 32,
        "id": "A32-022",
        "source_ids": [
          "A32-022"
        ],
        "anchor": "A32-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для длинного кабеля внутри мастерской можно рассматривать RS-485 или CAN с подходящими трансиверами, топологией и защитой. UART — логический интерфейс MCU, а не синоним RS-485. Собственная семантика Koobique может переноситься через такой адаптер, но нужны адресация, арбитраж/доставка и соответствующий профиль ошибок.",
        "y": 343.4557189941406,
        "page": 109,
        "old": 32,
        "id": "A32-023",
        "source_ids": [
          "A32-023"
        ],
        "anchor": "A32-023",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Проводная шина не становится безопасной из-за отсутствия Wi-Fi. Физический доступ, ошибки адресации, ground loop и индустриальные помехи создают другие риски. Изоляция и терминация выбираются по конкретной линии.",
        "y": 431.83465576171875,
        "page": 109,
        "old": 32,
        "id": "A32-024",
        "source_ids": [
          "A32-024"
        ],
        "anchor": "A32-024",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Интеграции с внешними платформами",
        "y": 495.6538391113281,
        "page": 109,
        "old": 32,
        "id": "A32-025",
        "source_ids": [
          "A32-025"
        ],
        "anchor": "A32-025",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "MQTT, Matter, Home Assistant, OPC UA или ROS-подобные окружения могут подключаться через адаптеры, когда появится реальная задача. Внутренние данные не должны потерять качество, время и права при переводе. Команда из сторонней системы проходит тот же арбитр, а не отдельный обходной путь.",
        "y": 522.6714477539062,
        "page": 109,
        "old": 32,
        "id": "A32-026",
        "source_ids": [
          "A32-026"
        ],
        "anchor": "A32-026",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Правило расширения",
        "y": 611.3452758789062,
        "page": 109,
        "old": 32,
        "id": "A32-027",
        "source_ids": [
          "A32-027"
        ],
        "anchor": "A32-027",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Каждая новая возможность обязана ответить на пять вопросов: что измеряется; какой исполнитель доступен; как проверяется результат; что происходит при потере связи; какие ограничения нельзя нарушать. Если ответы отсутствуют, интеграция ещё не готова к постоянной работе.",
        "y": 629.8064575195312,
        "page": 109,
        "old": 32,
        "id": "A32-028",
        "source_ids": [
          "A32-028"
        ],
        "anchor": "A32-028",
        "content_status": "active"
      },
      {
        "kind": "lead",
        "text": "Масштабируется язык наблюдений и действий. Законы процесса, датчики и допустимый риск не переносятся автоматически.",
        "id": "I17-001",
        "old": "I17",
        "source_ids": [
          "I17-001"
        ],
        "anchor": "I17-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Температура: сначала динамика, потом регулирование",
        "id": "I17-002",
        "old": "I17",
        "source_ids": [
          "I17-002"
        ],
        "anchor": "I17-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для одной условной тепловой зоны можно начать с модели",
        "id": "I17-003",
        "old": "I17",
        "source_ids": [
          "I17-003"
        ],
        "anchor": "I17-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "KQMATH042TOKEN",
        "id": "I17-004",
        "old": "I17",
        "source_ids": [
          "I17-004"
        ],
        "anchor": "I17-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "KQMATH043TOKEN — эффективная теплоёмкость, KQMATH044TOKEN — тепловое сопротивление, KQMATH045TOKEN — внутреннее выделение тепла, KQMATH046TOKEN — управляемый источник. Это приближение, не полная модель кабинета. Без независимых наблюдений некоторые параметры могут быть неразличимы.",
        "id": "I17-005",
        "old": "I17",
        "source_ids": [
          "I17-005"
        ],
        "anchor": "I17-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для текущего стенда нет основания считать комнатный вентилятор гарантированным охладителем воздуха: он может менять перемешивание и ощущения, не отводя тепло за пределы помещения. Чтобы модель обещала снижение температуры, нужна реальная связь с теплоотводом или воздухообменом. Вентиляторы самого куба обслуживают вычислитель и не должны становиться «климатическим исполнительным устройством» по сходству названия.",
        "id": "I17-006",
        "old": "I17",
        "source_ids": [
          "I17-006"
        ],
        "anchor": "I17-006",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I17",
        "caption": "Одна системная основа, разные пакеты процесса. Новый домен добавляет собственные измерения, модели, ограничения и испытания, а не только новое имя устройства.",
        "id": "I17-007",
        "old": "I17",
        "source_ids": [
          "I17-007"
        ],
        "anchor": "I17-007",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Присутствие в нескольких зонах",
        "id": "I17-008",
        "old": "I17",
        "source_ids": [
          "I17-008"
        ],
        "anchor": "I17-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Следующий сценарий — рабочий стол и проход. Он требует модели пространства: один сигнал не всегда позволяет определить, где находится человек. Можно использовать дополнительные датчики или ограничить обещание системы до общего присутствия. Камера — отдельный сознательный выбор, а не обязательное условие интеллектуальности.",
        "id": "I17-009",
        "old": "I17",
        "source_ids": [
          "I17-009"
        ],
        "anchor": "I17-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для нескольких людей появляются совместные предпочтения и права. Присутствие не означает согласие на запись голоса. Предполагаемая идентичность по голосу не выдаёт административные полномочия. Команда гостя на свет и запрос владельца на удаление истории относятся к разным классам доступа.",
        "id": "I17-010",
        "old": "I17",
        "source_ids": [
          "I17-010"
        ],
        "anchor": "I17-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Мастерская и наблюдение за оборудованием",
        "id": "I17-011",
        "old": "I17",
        "source_ids": [
          "I17-011"
        ],
        "anchor": "I17-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Полезные расширения: температура корпуса инструмента, вибрация, ток, открытие крышки, уровень шума. Сначала задача — обнаружить изменение относительно собственной нормальной работы и показать основания. Прогноз «возможно обслуживание» не равен диагностике конкретной неисправности.",
        "id": "I17-012",
        "old": "I17",
        "source_ids": [
          "I17-012"
        ],
        "anchor": "I17-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Если появляются опасные движущиеся части, критический контур не переносится в Wi-Fi-сервер. Нужны локальные межблокировки, подходящее оборудование и отдельная инженерная оценка. Koobique может собирать данные и давать рекомендации, не выдавая себя за сертифицированную систему безопасности.",
        "id": "I17-013",
        "old": "I17",
        "source_ids": [
          "I17-013"
        ],
        "anchor": "I17-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Носимые узлы",
        "id": "I17-014",
        "old": "I17",
        "source_ids": [
          "I17-014"
        ],
        "anchor": "I17-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Кнопка, инерциальный сенсор и локальная вибрация расширяют наблюдения о взаимодействии с человеком. Для них особенно важны энергобюджет, буфер при отсутствии станции, временная синхронизация и приватность. Досланное событие не становится текущей командой.",
        "id": "I17-015",
        "old": "I17",
        "source_ids": [
          "I17-015"
        ],
        "anchor": "I17-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Движение, неподвижность или температура корпуса носимого устройства — не готовая медицинская интерпретация. В рамках этой архитектуры не заявляем медицинских измерений. Такие применения потребуют самостоятельной аппаратной, клинической и нормативной работы.",
        "id": "I17-016",
        "old": "I17",
        "source_ids": [
          "I17-016"
        ],
        "anchor": "I17-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Мультиинсталляция",
        "id": "I17-017",
        "old": "I17",
        "source_ids": [
          "I17-017"
        ],
        "anchor": "I17-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Соседняя комната может получить второй радиошлюз, не становясь вторым независимым владельцем реле. Для первого расширения сохраняем один авторитетный исполнитель. Между домами данные передаются только с явной областью доступа; объединять личные истории в единое облако не требуется.",
        "id": "I17-018",
        "old": "I17",
        "source_ids": [
          "I17-018"
        ],
        "anchor": "I17-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Модель можно переносить как начальное предположение, но она снова проходит проверку на новом месте. Свежесть, единицы, типы и журнал переносятся хорошо. Коэффициент светильника и профиль присутствия — лишь кандидаты до подтверждения.",
        "id": "I17-019",
        "old": "I17",
        "source_ids": [
          "I17-019"
        ],
        "anchor": "I17-019",
        "content_status": "active"
      },
      {
        "kind": "quote",
        "text": "Новый пакет получает собственные измерения, допустимые действия, ограничения и испытания. Расширение исполнительных полномочий происходит после проверки нового физического процесса.",
        "id": "I17-020",
        "old": "I17",
        "source_ids": [
          "I17-020"
        ],
        "anchor": "I17-020",
        "content_status": "active"
      },
      {
        "id": "R45-001",
        "kind": "h2",
        "text": "Расширение определяется риском физического действия",
        "source_ids": [
          "R45-001"
        ],
        "anchor": "R45-001",
        "content_status": "active"
      },
      {
        "id": "R45-002",
        "kind": "p",
        "text": "Свет кабинета, насос, нагреватель и замок не получают одинаковый профиль отказа. Дополнение capability не переносит автоматически допуск первой лампы. Для каждой области заново определяются независимое отключение, наблюдаемость эффекта, допустимое время ON, механические ограничения и сценарий потери связи. Исследовательский пакет может оставаться в книге и работать на записи, пока физический контур ещё не допущен.",
        "source_ids": [
          "R45-002"
        ],
        "anchor": "R45-002",
        "content_status": "active"
      }
    ],
    "origins": [
      "A32",
      "I17"
    ],
    "question": "Что меняется при переходе к другой физической задаче?",
    "intro": "Новая комната не обязательно требует нового ядра, но почти всегда требует проверки размещения, коэффициентов и поведения при отказе. Температурный процесс медленнее света; носимое устройство спит и покидает сеть; несколько станций создают конфликт владения.\n\nАрхитектура масштабируется через явные контракты этих различий. Универсальность не означает, что каждое устройство обязано использовать один транспорт или что любая модель подходит любой нагрузке.",
    "id": "ch-45",
    "slug": "chapter-45"
  },
  {
    "number": 46,
    "title": "Как доказать, что система работает",
    "group": "Испытания",
    "units": [
      {
        "kind": "lead",
        "text": "Прототип сопровождается повторяемой проверкой, которая позволяет отличить улучшение от регрессии и воспроизвести результат демонстрации.",
        "y": 168.675537109375,
        "page": 103,
        "old": 31,
        "id": "A31-001",
        "source_ids": [
          "A31-001"
        ],
        "anchor": "A31-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Тесты делятся на расчётные, программные, аппаратные и сквозные. Модель подтверждает поведение на заданных предположениях. Unit-тест проверяет функцию. HIL соединяет реальное устройство и управляемый сценарий. Только совместное испытание показывает, что питание, радио, протокол, Core и физический выход работают вместе.",
        "y": 219.78143310546875,
        "page": 103,
        "old": 31,
        "id": "A31-002",
        "source_ids": [
          "A31-002"
        ],
        "anchor": "A31-002",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A38",
        "caption": "Стенд до переноса в корпус. Раздельный доступ к узлам позволяет измерять и локализовать ошибки.",
        "y": 299.9540710449219,
        "page": 103,
        "old": 31,
        "id": "A31-003",
        "source_ids": [
          "A31-003"
        ],
        "anchor": "A31-003",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Измерение задержек",
        "y": 657.1746826171875,
        "page": 103,
        "old": 31,
        "id": "A31-004",
        "source_ids": [
          "A31-004"
        ],
        "anchor": "A31-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "На клиенте фиксируются начало команды и получение результата по одним монотонным часам. Это даёт полный RTT. На станции отдельно измеряются очередь, проверка, транзакция и передача. На устройстве — время до изменения выхода. Внешний логический анализатор может наблюдать диагностические GPIO обеих сторон, если они доступны в одном безопасном стенде.",
        "y": 684.1923217773438,
        "page": 103,
        "old": 31,
        "id": "A31-005",
        "source_ids": [
          "A31-005"
        ],
        "anchor": "A31-005",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "A39",
        "caption": "Измерения на разных часах требуют оценки синхронизации. Полный RTT клиента и локальные интервалы надёжнее необоснованного вычитания UTC двух устройств.",
        "y": 56.69291687011719,
        "page": 104,
        "old": 31,
        "id": "A31-006",
        "source_ids": [
          "A31-006"
        ],
        "anchor": "A31-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Не следует выдавать RTT за одностороннюю задержку или измерять только быстрые успешные команды. В отчёт входят ошибки, тайм-ауты, p50/p95/p99 и худшие случаи. Выборка должна включать старт, длительную работу, массовое переподключение и конкурентные задачи.",
        "y": 384.31134033203125,
        "page": 104,
        "old": 31,
        "id": "A31-007",
        "source_ids": [
          "A31-007"
        ],
        "anchor": "A31-007",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Предлагаемые цели, а не полученные результаты",
        "y": 464.1054992675781,
        "page": 104,
        "old": 31,
        "id": "A31-008",
        "source_ids": [
          "A31-008"
        ],
        "anchor": "A31-008",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Проверка",
            "Исходная цель стенда",
            "Условия и оговорки"
          ],
          [
            "Команда → подтверждение постоянно доступного узла",
            "p95 до 100 мс, p99 до 250 мс",
            "Исправная локальная сеть, без механически медленного процесса"
          ],
          [
            "Внутренняя обработка Core",
            "p95 до 5 мс без обязательной fsync",
            "Отдельно измеряется надёжное принятие"
          ],
          [
            "Голос после конца фразы",
            "Подобрать целевой порог по тестам моделей",
            "Зависит от STT и endpointing; не обещается заранее"
          ],
          [
            "Нагрузка ядра",
            "100 устройств, 10 клиентов, 100 событий/с",
            "Синтетический запас, не требование Wi-Fi к одному радио"
          ],
          [
            "Длительная устойчивость",
            "Несколько суток непрерывного стенда",
            "С повторениями сети, записи и голоса"
          ]
        ],
        "y": 493.22,
        "page": 104,
        "old": 31,
        "id": "A31-009",
        "source_ids": [
          "A31-009"
        ],
        "anchor": "A31-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Это начальные инженерные ориентиры. Если плохая задержка вызвана неправильной семантикой подтверждения или неограниченной очередью, её не исправляют покупкой более быстрого CPU. Если ограничение честно физическое — например, длительное интегрирование датчика, — меняется профиль измерения, а не только код.",
        "y": 57.42815399169922,
        "page": 105,
        "old": 31,
        "id": "A31-010",
        "source_ids": [
          "A31-010"
        ],
        "anchor": "A31-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Матрица отказов",
        "y": 137.22232055664062,
        "page": 105,
        "old": 31,
        "id": "A31-011",
        "source_ids": [
          "A31-011"
        ],
        "anchor": "A31-011",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Воздействие",
            "Ожидаемое поведение",
            "Что фиксируется"
          ],
          [
            "Интернет выключен",
            "Локальные команды и правила работают",
            "Нет внешних обязательных запросов"
          ],
          [
            "Роутер выключен",
            "Кнопка и локальная безопасность работают",
            "Время восстановления связи"
          ],
          [
            "Перезапуск узла",
            "Новый boot_id, корректный snapshot",
            "Нет ложного повторного действия"
          ],
          [
            "Потеря ответа",
            "UNKNOWN или последующая сверка",
            "Тот же command_id при повторе"
          ],
          [
            "Датчик завис",
            "Канал недоступен, другие живы",
            "Тайм-аут и восстановление драйвера"
          ],
          [
            "Диск почти заполнен",
            "Сокращается необязательная история",
            "Критичная конфигурация сохраняется"
          ],
          [
            "Speech-worker убит",
            "Управление не останавливается",
            "Перезапуск без утечки ресурсов"
          ],
          [
            "Старый сертификат / чужой ключ",
            "Отказ с понятной причиной",
            "Нет fallback на небезопасный режим"
          ]
        ],
        "y": 166.34,
        "page": 105,
        "old": 31,
        "id": "A31-012",
        "source_ids": [
          "A31-012"
        ],
        "anchor": "A31-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для сетевого стенда Linux netem позволяет вводить задержки, потери, дублирование и изменение порядка там, где это применимо к тестируемому пути. Он не заменяет реальный радиоэфир, но делает часть отказов воспроизводимой. [85]",
        "y": 512.5888061523438,
        "page": 105,
        "old": 31,
        "id": "A31-013",
        "source_ids": [
          "A31-013"
        ],
        "anchor": "A31-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Парсеры, конкурентность и свойства",
        "y": 576.4080200195312,
        "page": 105,
        "old": 31,
        "id": "A31-014",
        "source_ids": [
          "A31-014"
        ],
        "anchor": "A31-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для KQIP нужны fuzz-тесты на длины, вложенность, неизвестные поля и повреждённые кадры. Cargo-fuzz помогает запускать такие испытания. Для небольших конкурентных примитивов Loom позволяет исследовать разные варианты выполнения. Эти инструменты находят классы ошибок, но не являются сертификатом отсутствия уязвимостей. [95][86]",
        "y": 603.4255981445312,
        "page": 105,
        "old": 31,
        "id": "A31-015",
        "source_ids": [
          "A31-015"
        ],
        "anchor": "A31-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Проверяются последовательности, а не только одиночный set: известный ID не меняет выход после ручного вмешательства; срок запрещает новое действие, но не чтение истории; старый snapshot не заменяет новый; UNKNOWN не равен EMPTY; автоматизация не нарушает ManualOverride. Каждый успешный counterexample превращается в постоянный регрессионный тест.",
        "y": 691.8045654296875,
        "page": 105,
        "old": 31,
        "id": "A31-016",
        "source_ids": [
          "A31-016"
        ],
        "anchor": "A31-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Электрические измерения",
        "y": 56.314456939697266,
        "page": 106,
        "old": 31,
        "id": "A31-017",
        "source_ids": [
          "A31-017"
        ],
        "anchor": "A31-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сначала измеряются питание и сигналы, затем сетевое поведение. Для реле проверяются стартовый импульс, ток катушки, выброс на ключе и контактная операция. Для I²C — уровни, фронты и ошибки при подключении второго сенсора. Для UART — правильная скорость, framing и реакция на неполный кадр.",
        "y": 83.33208465576172,
        "page": 106,
        "old": 31,
        "id": "A31-018",
        "source_ids": [
          "A31-018"
        ],
        "anchor": "A31-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для датчиков сохраняются raw и опорные условия. Для вентиляции — температура комнаты и реальные обороты. Тест с грязным фильтром не должен создавать опасную пылевую среду; используется контролируемое изменение сопротивления под наблюдением.",
        "y": 155.73602294921875,
        "page": 106,
        "old": 31,
        "id": "A31-019",
        "source_ids": [
          "A31-019"
        ],
        "anchor": "A31-019",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Реальные пользователи и эксплуатация",
        "y": 235.53021240234375,
        "page": 106,
        "old": 31,
        "id": "A31-020",
        "source_ids": [
          "A31-020"
        ],
        "anchor": "A31-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Второй человек подключает узел и выполняет восстановление по инструкции. Вопросы и места остановки записываются, а не компенсируются устной подсказкой автора. Через несколько дней проверяется, удобно ли увидеть причину включения и отключить правило, которое мешает работе.",
        "y": 262.5478515625,
        "page": 106,
        "old": 31,
        "id": "A31-021",
        "source_ids": [
          "A31-021"
        ],
        "anchor": "A31-021",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Минимальный пакет результата",
        "y": 351.2216796875,
        "page": 106,
        "old": 31,
        "id": "A31-022",
        "source_ids": [
          "A31-022"
        ],
        "anchor": "A31-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для каждого теста сохранить commit, hardware_rev, конфигурацию, исходные данные, критерий, результат и ссылку на лог. Снимок экрана с надписью «успешно» без условий не является достаточным инженерным доказательством.",
        "y": 369.682861328125,
        "page": 106,
        "old": 31,
        "id": "A31-023",
        "source_ids": [
          "A31-023"
        ],
        "anchor": "A31-023",
        "content_status": "active"
      },
      {
        "kind": "lead",
        "text": "Полезность управления оценивается по результату в комнате. Качество текста и точность модели измеряются отдельно и связываются с физическим результатом через испытание всего контура.",
        "id": "I23-001",
        "old": "I23",
        "source_ids": [
          "I23-001"
        ],
        "anchor": "I23-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Три сравнения",
        "id": "I23-002",
        "old": "I23",
        "source_ids": [
          "I23-002"
        ],
        "anchor": "I23-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сначала проверяем отдельный компонент: ошибку температуры, распознавание фразы, качество оценки присутствия. Затем проверяем контур: достижение цели, задержку, число переключений, соблюдение ручного режима. Наконец — эксплуатацию: восстановление, потерю канала, ресурсный пик и перенос размещения.",
        "id": "I23-003",
        "old": "I23",
        "source_ids": [
          "I23-003"
        ],
        "anchor": "I23-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для каждого AI-кандидата есть разумный не-AI эталон. Если классификатор выигрывает среднюю точность, но чаще гасит свет при неподвижном человеке, он не проходит допуск по выбранной цене ошибки. Низкая ошибка одношагового прогноза также не гарантирует хорошую длинную траекторию — это различие существенно для идентификации моделей управления. [111]",
        "id": "I23-004",
        "old": "I23",
        "source_ids": [
          "I23-004"
        ],
        "anchor": "I23-004",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Матрица обязательных эпизодов",
        "id": "I23-005",
        "old": "I23",
        "source_ids": [
          "I23-005"
        ],
        "anchor": "I23-005",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Условие",
            "Проверяемое поведение"
          ],
          [
            "Неподвижный человек",
            "Нет вывода о пустоте только по отсутствию PIR"
          ],
          [
            "Пустая комната, помеха",
            "Ложные свидетельства не превращаются в безусловную уверенность"
          ],
          [
            "Датчик закрыт/перенесён",
            "Модель не считает старую калибровку доказанно действующей"
          ],
          [
            "Потеря пакетов и часов",
            "Пропуски и неопределённое время явно отражены"
          ],
          [
            "Ручной OFF во время плана",
            "Старое предложение не отменяет ручное действие"
          ],
          [
            "Обрыв нагрузки",
            "Подтверждение выхода не объявляет физическую цель достигнутой"
          ],
          [
            "AI-worker завис",
            "Базовый контроль и доступная локальная кнопка сохраняются"
          ],
          [
            "Модель обновлена/откачена",
            "Восстанавливается совместимый комплект данных и преобразований"
          ]
        ],
        "id": "I23-006",
        "old": "I23",
        "source_ids": [
          "I23-006"
        ],
        "anchor": "I23-006",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I23",
        "caption": "Пирамида доказательств. Симуляция ускоряет поиск ошибок, но не заменяет электрическое испытание, а несколько удачных демонстраций не подтверждают перенос в новые условия.",
        "id": "I23-007",
        "old": "I23",
        "source_ids": [
          "I23-007"
        ],
        "anchor": "I23-007",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Вероятности и покрытие",
        "id": "I23-008",
        "old": "I23",
        "source_ids": [
          "I23-008"
        ],
        "anchor": "I23-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Вероятностный прогноз оцениваем и по точности, и по покрытию интервалов. Не проверяем только средний показатель по смеси лёгких и трудных режимов. Отдельно смотрим на перенос, пропуски и смену датчика.",
        "id": "I23-009",
        "old": "I23",
        "source_ids": [
          "I23-009"
        ],
        "anchor": "I23-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Адаптивные conformal-методы исследуют поддержание покрытия при изменении распределения. [129] Их статистический смысл зависит от постановки и не является гарантией безопасности каждого отдельного физического действия. Для опасного последствия недостаточно поместить рядом с моделью слово «калиброванная».",
        "id": "I23-010",
        "old": "I23",
        "source_ids": [
          "I23-010"
        ],
        "anchor": "I23-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Ноль ошибок — не нулевой риск",
        "id": "I23-011",
        "old": "I23",
        "source_ids": [
          "I23-011"
        ],
        "anchor": "I23-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "При условии независимых одинаково распределённых испытаний и нуле ошибок в KQMATH051TOKEN попытках односторонняя 95-процентная верхняя граница вероятности ошибки равна KQMATH052TOKEN. Для KQMATH053TOKEN это около 0,99%. Это расчётная иллюстрация: соседние секунды одной записи не дают 300 независимых эпизодов.",
        "id": "I23-012",
        "old": "I23",
        "source_ids": [
          "I23-012"
        ],
        "anchor": "I23-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Поэтому фиксируем объём и разнообразие тестов, а не только красивый процент. Для проекта на раннем этапе важнее открытое описание ещё не испытанных режимов, чем псевдоточная оценка «надёжность 99,999%».",
        "id": "I23-013",
        "old": "I23",
        "source_ids": [
          "I23-013"
        ],
        "anchor": "I23-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Инструменты испытания",
        "id": "I23-014",
        "old": "I23",
        "source_ids": [
          "I23-014"
        ],
        "anchor": "I23-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Начальный симулятор света работает без тяжёлых зависимостей. Для дальнейших тепловых процессов можно изучить FMI для обмена моделями и BOPTEST для сопоставления контроллеров на моделях зданий. [139] [140] Перенос такой симуляции в свой кабинет требует сопоставления измерений и параметров.",
        "id": "I23-015",
        "old": "I23",
        "source_ids": [
          "I23-015"
        ],
        "anchor": "I23-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Property-based тесты проверяют инварианты контрактов; fuzzing — парсеры и границы ресурсов; повтор журналов — последовательности отказов. Аппаратный стенд добавляет питание, электрические уровни и физическую нагрузку. Назначение этих уровней различается: успешный fuzzing не подтверждает, что реле подключено правильно.",
        "id": "I23-016",
        "old": "I23",
        "source_ids": [
          "I23-016"
        ],
        "anchor": "I23-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Перед допуском модели проверяют цену ошибок, отказ от решения и устойчивость замкнутого контура. Средний результат benchmark дополняет эти проверки, но не определяет допуск самостоятельно.",
        "id": "I23-017",
        "old": "I23",
        "source_ids": [
          "I23-017"
        ],
        "anchor": "I23-017",
        "content_status": "active"
      },
      {
        "id": "N-033",
        "kind": "h2",
        "text": "Результат, который можно повторить",
        "new": true,
        "source_ids": [
          "N-033"
        ],
        "anchor": "N-033",
        "content_status": "active"
      },
      {
        "id": "N-034",
        "kind": "p",
        "text": "К каждому испытанию прикладываются конфигурация, версии, исходные данные, последовательность действий и критерий успеха. Для оборудования добавляются ревизии плат, схема соединений и условия среды; для моделей — хеш артефакта, разбиение эпизодов и способ исполнения.",
        "new": true,
        "source_ids": [
          "N-034"
        ],
        "anchor": "N-034",
        "content_status": "active"
      },
      {
        "id": "N-035",
        "kind": "p",
        "text": "Так неудача становится полезной. Вместо «иногда выключается свет» получается воспроизводимая история: при таком состоянии, такой очереди и таком обрыве старая команда прошла проверку. Исправление сопровождается тестом на эту историю и проверкой, что обычный сценарий не изменился неожиданно.",
        "new": true,
        "source_ids": [
          "N-035"
        ],
        "anchor": "N-035",
        "content_status": "active"
      },
      {
        "id": "R46-001",
        "kind": "h2",
        "text": "Измерительные границы и стартовые цели",
        "source_ids": [
          "R46-001"
        ],
        "anchor": "R46-001",
        "content_status": "active"
      },
      {
        "id": "R46-002",
        "kind": "table",
        "rows": [
          [
            "Проверка",
            "Начальная цель / выборка",
            "Что не следует из PASS"
          ],
          [
            "Команда доступному узлу",
            "1000 попыток на условие; p95 ≤100 мс, p99 ≤250 мс, все тайм-ауты учтены",
            "Не доказывает скорость обнаружения входа"
          ],
          [
            "Физический вход → свет",
            "Отдельная разметка момента входа, детектора и выхода",
            "Нельзя вычесть часы двух плат без привязки"
          ],
          [
            "Долговечное ACCEPTED",
            "Клиентский запрос → commit → ответ, включая fsync",
            "5 мс чистого Core не заменяют эту метрику"
          ],
          [
            "Soak",
            "72 ч; 0 необъяснённых reboot; нет монотонного роста FD/очередей",
            "Не доказывает многолетнюю надёжность"
          ],
          [
            "Потеря питания / MCU",
            "50 управляемых циклов на тестовом стенде, затем анализ reset и выхода",
            "Не подтверждает все источники и партии"
          ],
          [
            "RU/EN push-to-talk",
            "300–500 отдельных фраз на язык; 500 негативов; отдельный тестовый набор",
            "Нельзя объявить нулевую вероятность ошибки"
          ],
          [
            "Тепло / радио",
            "2 ч после установления; закрытый корпус, заданная комната и Wi-Fi",
            "RSSI сам по себе не является критерием качества"
          ]
        ],
        "source_ids": [
          "R46-002"
        ],
        "anchor": "R46-002",
        "content_status": "active"
      },
      {
        "id": "R46-003",
        "kind": "p",
        "text": "p99 на тысяче попыток опирается примерно на десять крайних случаев и требует интервала неопределённости, а не только красивой цифры. Ошибки, отмены и тайм-ауты входят в знаменатель. Если не было ни одного ложного действия на 500 независимых негативах, верхняя односторонняя 95% граница всё ещё около 0,6%. Коррелированные записи уменьшают информативность числа опытов.",
        "source_ids": [
          "R46-003"
        ],
        "anchor": "R46-003",
        "content_status": "active"
      },
      {
        "id": "R46-004",
        "kind": "p",
        "text": "Точность температуры сравнивается с эталоном известной неопределённости в нескольких установившихся точках; цель ±0,3 °C относится только к согласованному измерению при достаточном эталоне, не к любой точке комнаты. Присутствие проверяется неподвижным сидением 30 минут, помехами и потерей каналов. Для PIR-only ожидаемый результат — отсутствие необоснованного OFF.",
        "source_ids": [
          "R46-004"
        ],
        "anchor": "R46-004",
        "content_status": "active"
      },
      {
        "id": "R46-005",
        "kind": "h2",
        "text": "Регрессии, без которых E0 не получает исполнительный допуск",
        "source_ids": [
          "R46-005"
        ],
        "anchor": "R46-005",
        "content_status": "active"
      },
      {
        "id": "R46-006",
        "kind": "p",
        "text": "Обязательны: A/потеря ACK/кнопка/повтор; тот же ID с другим телом; повтор после вытеснения кэша; reboot между GPIO и результатом; первая доставка после срока; suspend H0 на 10 минут; UTC ±1 год; новый план при MANUAL_HOLD; поздний ACK после OFF; старый backup после переноса; отзыв при открытой сессии; диск заполнен; AI-worker завис; радар пропал. Каждый опыт сохраняет трассу и итоговое состояние, а не только PASS.",
        "source_ids": [
          "R46-006"
        ],
        "anchor": "R46-006",
        "content_status": "active"
      }
    ],
    "origins": [
      "A31",
      "I23"
    ],
    "question": "Какие испытания отличают демонстрацию от работающей системы?",
    "intro": "Один удачный голосовой запрос показывает только один удачный запрос. Нам нужны повторяемые проверки обычных действий, нагрузочных пиков и отказов: перезагрузка, дубликат, устаревшее измерение, полный диск, повреждённое обновление.\n\nФиксируем не только среднее время, но и редкие задержки, ресурсные пределы и условия опыта. Для моделей отдельно проверяем перенос в новые ситуации и цену ошибок. Ноль ошибок в небольшой выборке не является доказательством нулевого риска.",
    "id": "ch-46",
    "slug": "chapter-46"
  },
  {
    "number": 47,
    "title": "Что покупать и в каком порядке",
    "group": "Комплектующие",
    "units": [
      {
        "kind": "lead",
        "text": "Ведомость описывает назначение и совместимость. Наличие на маркетплейсе проверяется для конкретной ревизии перед покупкой.",
        "y": 168.675537109375,
        "page": 110,
        "old": 33,
        "id": "A33-001",
        "source_ids": [
          "A33-001"
        ],
        "anchor": "A33-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первый заказ должен позволить пройти сквозной низковольтный сценарий на имеющемся ПК. Большой куб, GPU, массив микрофонов и собственные платы не нужны в одной первой корзине. Это не экономия на качестве: последовательная покупка уменьшает число одновременно неизвестных компонентов.",
        "y": 219.78143310546875,
        "page": 110,
        "old": 33,
        "id": "A33-002",
        "source_ids": [
          "A33-002"
        ],
        "anchor": "A33-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Заказ A — измерительный и исполнительный стенд",
        "y": 299.5755920410156,
        "page": 110,
        "old": 33,
        "id": "A33-003",
        "source_ids": [
          "A33-003"
        ],
        "anchor": "A33-003",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Позиция",
            "Количество",
            "Критерий выбора",
            "Комментарий"
          ],
          [
            "ESP32-C6-DevKitC-1 N8",
            "4",
            "Документированная плата/ревизия, USB-данные",
            "Три узла + запасная плата"
          ],
          [
            "SHT45 breakout",
            "1",
            "Схема, питание, логические уровни",
            "Допустим SHT4x другого класса с учётом точности"
          ],
          [
            "VEML7700 breakout",
            "1",
            "Документированные подтяжки и VIN",
            "OPT3001 — отдельная альтернативная ветка"
          ],
          [
            "PIR-модуль",
            "1",
            "Известный выходной уровень и настройка",
            "Для первого события входа"
          ],
          [
            "Радар Presence",
            "1, второй этап A",
            "Полный UART-протокол и питание",
            "SEN0395 — документированный ориентир"
          ],
          [
            "Релейный модуль",
            "1",
            "Совместимый SIG, драйвер и защитный диод",
            "Не голая катушка на GPIO"
          ],
          [
            "Лампа 12 В",
            "1",
            "Готова к указанному источнику питания",
            "Начать с небольшой мощности"
          ],
          [
            "Адаптер 12 В",
            "1",
            "Закрытый, надёжный, с запасом тока",
            "Отдельно от логики"
          ],
          [
            "Предохранитель и держатель",
            "1 комплект",
            "DC-параметры под провод и нагрузку",
            "Устанавливается у источника"
          ]
        ],
        "y": 328.69,
        "page": 110,
        "old": 33,
        "id": "A33-004",
        "source_ids": [
          "A33-004"
        ],
        "anchor": "A33-004",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Позиция",
            "Количество",
            "Критерий выбора",
            "Комментарий"
          ],
          [
            "USB-кабели с данными",
            "3–4",
            "Качество контактов и длина",
            "Для прошивки и диагностики; совместное питание с адаптером требует отдельной проверки"
          ],
          [
            "Разъёмы, провод, монтажная плата",
            "Набор",
            "Под уровни и токи",
            "Для ухода от ненадёжных перемычек"
          ],
          [
            "Измеритель тока INA226",
            "1, опция",
            "Шунт и схема известны",
            "После базового реле"
          ],
          [
            "Закрытый источник 5 В для Actuator",
            "1",
            "Выход, запас по пиковому току радио и реле, вход платы и разъём согласованы с точной ревизией",
            "Независим от Hub; номинал тока определяется выбранными деталями и измерением"
          ],
          [
            "Питание Sensor и Presence",
            "2 согласованных питающих канала",
            "Отдельные подходящие источники либо документированный многопортовый источник; VIN каждого модуля подтверждён схемой",
            "Ведомость фиксирует источники, кабели и распределение токов; USB от Hub не подставляется молча"
          ],
          [
            "Жгут 5 В и сервисное подключение Actuator",
            "1 комплект",
            "Штатный вход DevKit, обратный провод, разъёмы, сечение и исключение обратного питания проверены",
            "Допустимые сочетания внешнего источника и USB документируются по карте R16-003"
          ]
        ],
        "y": 56.69,
        "page": 111,
        "old": 33,
        "id": "A33-005",
        "source_ids": [
          "A33-005"
        ],
        "anchor": "A33-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Документация базовых плат и модулей приведена в источниках; внешний вид карточки магазина не заменяет её. Неизвестный модуль с той же микросхемой может иметь иную схему питания и распиновку. [14][16][18][22][25][79]",
        "y": 218.97250366210938,
        "page": 111,
        "old": 33,
        "id": "A33-006",
        "source_ids": [
          "A33-006"
        ],
        "anchor": "A33-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Заказ B — самостоятельный вычислитель",
        "y": 282.79168701171875,
        "page": 111,
        "old": 33,
        "id": "A33-007",
        "source_ids": [
          "A33-007"
        ],
        "anchor": "A33-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для H1 покупается один мини-ПК после проверки Linux и голоса. Для H2 заказываются Mini-ITX-плата, совместимый CPU, RAM, SSD, кулер и штатный БП. Одновременно покупать H1, Pi и Jetson не требуется: сравнение сначала выполняется по документам и доступным измерениям, затем выбирается одна основа.",
        "y": 309.809326171875,
        "page": 111,
        "old": 33,
        "id": "A33-008",
        "source_ids": [
          "A33-008"
        ],
        "anchor": "A33-008",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Узел H2",
            "Исходный ориентир",
            "Проверить до оплаты"
          ],
          [
            "Плата",
            "Mini-ITX AM5 с Wi-Fi и fan headers",
            "CPU support, BIOS, радио, I/O"
          ],
          [
            "CPU",
            "Класс Ryzen 7 8700G",
            "Точная модель, комплектность, охлаждение"
          ],
          [
            "RAM",
            "2 × 16 ГБ, штатные параметры",
            "QVL и доступность слотов"
          ],
          [
            "SSD",
            "NVMe 1 ТБ",
            "Формат, температура, гарантия"
          ],
          [
            "Кулер",
            "Совместимый, около 125 мм для опорной геометрии",
            "Высота от CPU, RAM и VRM"
          ],
          [
            "БП",
            "Готовый SFX или иной согласованный формат",
            "Родные кабели и объём монтажа"
          ],
          [
            "FAN",
            "Три 120-мм PWM",
            "Разъём, ток, толщина, отверстия"
          ]
        ],
        "y": 384.31,
        "page": 111,
        "old": 33,
        "id": "A33-009",
        "source_ids": [
          "A33-009"
        ],
        "anchor": "A33-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Заказ C — механика",
        "y": 654.0836791992188,
        "page": 111,
        "old": 33,
        "id": "A33-010",
        "source_ids": [
          "A33-010"
        ],
        "anchor": "A33-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для рамы 320 мм по описанной стыковке: 4 профиля по 320 мм и 8 по 280 мм, соответствующие соединители, крепёж, ножки и отдельные панели. Длина уже нарезанного профиля проверяется штангенциркулем/угольником; ошибка одного элемента влияет на весь корпус.",
        "y": 681.101318359375,
        "page": 111,
        "old": 33,
        "id": "A33-011",
        "source_ids": [
          "A33-011"
        ],
        "anchor": "A33-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Печатные детали сначала делают как тестовые купоны и небольшие узлы: вставки, фильтр, держатель кабеля, уголок. Итоговая панель или сложная «корона» выпускается после проверки посадок. Раскрой металла следует оплачивать после габаритного макета и уточнения кабелей.",
        "y": 57.42815399169922,
        "page": 112,
        "old": 33,
        "id": "A33-012",
        "source_ids": [
          "A33-012"
        ],
        "anchor": "A33-012",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Заказ D — голос и собственная PCB",
        "y": 137.22232055664062,
        "page": 112,
        "old": 33,
        "id": "A33-013",
        "source_ids": [
          "A33-013"
        ],
        "anchor": "A33-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для голоса сначала используются телефон и Fifine. После сравнения моделей выбирается выносной массив или встроенный модуль. Динамик, усилитель и акустическая камера приобретаются как согласованный комплект, а не просто по максимальной мощности.",
        "y": 164.23995971679688,
        "page": 112,
        "old": 33,
        "id": "A33-014",
        "source_ids": [
          "A33-014"
        ],
        "anchor": "A33-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Собственная PCB заказывается после успешного паяного прототипа. Для первой партии достаточно нескольких носителей и одной версии схемы. Доставка плат из другого города обычно практичнее, чем ограничивать поиск только Тулой; конкретные услуги подтверждаются у подрядчика. [28][29][30]",
        "y": 236.64389038085938,
        "page": 112,
        "old": 33,
        "id": "A33-015",
        "source_ids": [
          "A33-015"
        ],
        "anchor": "A33-015",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Карточка покупки",
        "y": 316.4380798339844,
        "page": 112,
        "old": 33,
        "id": "A33-016",
        "source_ids": [
          "A33-016"
        ],
        "anchor": "A33-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для каждой позиции сохраняются точное название, фото маркировки, ревизия, продавец, ссылка на производителя, цена на дату заказа и допустимая замена. Если замена меняет электрический или механический параметр, она получает отметку «повторная проверка», а не просто строку «аналог».",
        "y": 343.4557189941406,
        "page": 112,
        "old": 33,
        "id": "A33-017",
        "source_ids": [
          "A33-017"
        ],
        "anchor": "A33-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Подозрительно низкая цена или копирование официального фото не подтверждают подлинность. Для важных узлов полезны продавец с документами, возможность возврата и проверяемая маркировка. Не нужно придумывать актуальную стоимость в исследовательской книге: ведомость станет сметой только после выбора конкретных предложений.",
        "y": 415.85968017578125,
        "page": 112,
        "old": 33,
        "id": "A33-018",
        "source_ids": [
          "A33-018"
        ],
        "anchor": "A33-018",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Что можно не покупать сейчас",
        "y": 520.5084838867188,
        "page": 112,
        "old": 33,
        "id": "A33-019",
        "source_ids": [
          "A33-019"
        ],
        "anchor": "A33-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Дискретную GPU, собственный RF-модуль на голом чипе, большой микрофонный массив, сетевое реле на 230 В и сложную многослойную плату. Ни одна из этих позиций не нужна, чтобы доказать первый полноценный сценарий Koobique.",
        "y": 538.9696655273438,
        "page": 112,
        "old": 33,
        "id": "A33-020",
        "source_ids": [
          "A33-020"
        ],
        "anchor": "A33-020",
        "content_status": "active"
      },
      {
        "id": "R47-001",
        "kind": "h2",
        "text": "До заказа: инвентаризация, затем один точный комплект",
        "source_ids": [
          "R47-001"
        ],
        "anchor": "R47-001",
        "content_status": "active"
      },
      {
        "id": "R47-002",
        "kind": "p",
        "text": "У автора уже есть инструменты, ПК, телефоны и микрофон. Поэтому «заказ 0» — проверка доступности лабораторного БП с ограничением тока, мультиметра, осциллографа/логического анализатора, ESD-защиты и безопасного отключения питания; отсутствующее покупается, имеющееся не дублируется без причины.",
        "source_ids": [
          "R47-002"
        ],
        "anchor": "R47-002",
        "content_status": "active"
      },
      {
        "id": "R47-003",
        "kind": "p",
        "text": "Для каждого выбранного модуля сохраняются производитель, точный артикул, ревизия, фото, datasheet, схема breakout и проверенный продавец. Маркетплейс показывает товар, но не доказывает идентичность платы. Запасная C6 нужна до необратимых опытов eFuse; повторяемая сборка E2 потребует дополнительных сенсоров и проверки замен. Бюджеты ревьюеров без актуальной ведомости не выдаются за цену Koobique.",
        "source_ids": [
          "R47-003"
        ],
        "anchor": "R47-003",
        "content_status": "active"
      }
    ],
    "origins": [
      "A33"
    ],
    "question": "Как покупать так, чтобы каждая посылка приближала работающий цикл?",
    "intro": "Первая закупка нужна для измерительного и исполнительного стенда, а не для заполнения красивого корпуса. Станция пока работает на имеющемся ПК. После проверки протокола и нагрузки выбираем отдельный вычислитель и только потом выпускаем окончательные детали оболочки.\n\nВедомость ниже описывает функции и проверяемые примеры, а не гарантированное наличие у конкретного продавца. При замене модуля заново проверяются питание, логические уровни, контакты, драйвер и механические размеры.",
    "id": "ch-47",
    "slug": "chapter-47"
  },
  {
    "number": 48,
    "title": "Первый и второй экземпляры",
    "group": "Сборка",
    "units": [
      {
        "kind": "lead",
        "text": "Каждый этап заканчивается работающей проверкой. Нельзя переносить неизвестную проблему в следующий слой конструкции.",
        "y": 168.675537109375,
        "page": 113,
        "old": 34,
        "id": "A34-001",
        "source_ids": [
          "A34-001"
        ],
        "anchor": "A34-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первый экземпляр E0 — открытый лабораторный стенд. E1 — самостоятельная станция в корпусе с теми же логическими контрактами. E2 — повторяемая сборка по документации, выполненная без памяти автора о «секретном проводке». Это не массовое производство, а последовательное снижение инженерной неопределённости.",
        "y": 219.78143310546875,
        "page": 113,
        "old": 34,
        "id": "A34-002",
        "source_ids": [
          "A34-002"
        ],
        "anchor": "A34-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Этап 0. Подготовка среды",
        "y": 299.5755920410156,
        "page": 113,
        "old": 34,
        "id": "A34-003",
        "source_ids": [
          "A34-003"
        ],
        "anchor": "A34-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Создать репозиторий документации, каталог аппаратных ревизий и единый журнал измерений. На имеющемся ПК поднять Linux-окружение, Rust toolchain и симулятор устройств. До подключения реального реле должны работать ID команды, журнал результата и сценарий потери соединения.",
        "y": 326.5932312011719,
        "page": 113,
        "old": 34,
        "id": "A34-004",
        "source_ids": [
          "A34-004"
        ],
        "anchor": "A34-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Результат этапа — воспроизводимый запуск и сохранённый манифест. Если сборка работает только из незадокументированной домашней директории, переходить к изготовлению корпуса рано.",
        "y": 398.9971618652344,
        "page": 113,
        "old": 34,
        "id": "A34-005",
        "source_ids": [
          "A34-005"
        ],
        "anchor": "A34-005",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Этап 1. Один датчик",
        "y": 462.8163146972656,
        "page": 113,
        "old": 34,
        "id": "A34-006",
        "source_ids": [
          "A34-006"
        ],
        "anchor": "A34-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Проверить ESP32 без периферии, затем один SHT-модуль. Снять питание, прозвонить, проверить напряжения, прочитать ID/результат и только потом добавить второй сенсор. Для VEML7700 проверить raw и настройки gain/integration, а не сразу график в приложении.",
        "y": 489.833984375,
        "page": 113,
        "old": 34,
        "id": "A34-007",
        "source_ids": [
          "A34-007"
        ],
        "anchor": "A34-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Результат — корректные наблюдения с единицами, возрастом и флагами. Сбой сенсора должен быть виден как сбой, не как нулевое значение. Фотография проводов и таблица соединений сохраняются.",
        "y": 562.2379150390625,
        "page": 113,
        "old": 34,
        "id": "A34-008",
        "source_ids": [
          "A34-008"
        ],
        "anchor": "A34-008",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Этап 2. Один исполнитель",
        "y": 626.05712890625,
        "page": 113,
        "old": 34,
        "id": "A34-009",
        "source_ids": [
          "A34-009"
        ],
        "anchor": "A34-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сначала релейный модуль без нагрузки, затем 12-вольтовая лампа через предохранитель. Проверить кнопку и стартовый уровень. Подключить протокол, повтор команды и потерю подтверждения. Только после этого вводить автоматическое управление.",
        "y": 653.07470703125,
        "page": 113,
        "old": 34,
        "id": "A34-010",
        "source_ids": [
          "A34-010"
        ],
        "anchor": "A34-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Результат — set(on/off) с известным состоянием при перезапуске и без опасного импульса. Измеритель тока добавляется как независимое расширение, чтобы можно было сравнить поведение до и после него.",
        "y": 709.503662109375,
        "page": 113,
        "old": 34,
        "id": "A34-011",
        "source_ids": [
          "A34-011"
        ],
        "anchor": "A34-011",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Этап 3. Presence и замкнутый световой сценарий",
        "y": 56.314456939697266,
        "page": 114,
        "old": 34,
        "id": "A34-012",
        "source_ids": [
          "A34-012"
        ],
        "anchor": "A34-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "В E0 подключить PIR и проверить включение при темноте, состояние UNKNOWN и ручной ManualOverride до явного освобождения. По отсутствию движения автоматический OFF запрещён. Проверить, что собственная лампа не вызывает самопереключение, а следующий вход не снимает ручной запрет. Для расширения E1 добавить радар, собрать размеченные эпизоды и отдельно допустить Presence-qualified: только этот профиль может выключать свет после подтверждённого ухода.",
        "y": 83.33208465576172,
        "page": 114,
        "old": 34,
        "id": "A34-013",
        "source_ids": [
          "A34-013"
        ],
        "anchor": "A34-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Результат — работающий кабинет на имеющемся ПК. Интернет отключается на время проверки. Это первый полноценный экземпляр экосистемы, даже если корпус станции ещё не изготовлен.",
        "y": 155.73602294921875,
        "page": 114,
        "old": 34,
        "id": "A34-014",
        "source_ids": [
          "A34-014"
        ],
        "anchor": "A34-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Этап 4. Мобильное управление и подключение",
        "y": 219.55520629882812,
        "page": 114,
        "old": 34,
        "id": "A34-015",
        "source_ids": [
          "A34-015"
        ],
        "anchor": "A34-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для пользовательского управления определить Client API Core и минимальные экраны: инсталляции, добавление, комната, узел, намерение и результат, диагностика. Доверенный локальный bootstrap нужен первому стенду; BLE-onboarding и целевой KMP-клиент Android/iOS развиваются в E1. До их допуска выполнить проверки на реальных телефонах, включая отказ разрешений, прерывание и смену сети. Выбранный интерфейс E0 должен быть прямо указан в манифесте стенда.",
        "y": 246.57284545898438,
        "page": 114,
        "old": 34,
        "id": "A34-016",
        "source_ids": [
          "A34-016"
        ],
        "anchor": "A34-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Результат — пользователь управляет системой из обычной домашней сети и понимает состояние ошибки. Точный визуальный стиль приложения разрабатывается отдельно по правилам KMP-проекта.",
        "y": 318.9767761230469,
        "page": 114,
        "old": 34,
        "id": "A34-017",
        "source_ids": [
          "A34-017"
        ],
        "anchor": "A34-017",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Этап 5. Голос и выбор вычислителя",
        "y": 382.7959289550781,
        "page": 114,
        "old": 34,
        "id": "A34-018",
        "source_ids": [
          "A34-018"
        ],
        "anchor": "A34-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Записать RU/EN-корпус и сравнить модели на ПК. Проверить намерения, отрицания и неоднозначные имена. Измерить память и задержку под одновременной нагрузкой Core. По результатам выбрать H1/H2/H3/H4.",
        "y": 409.8135681152344,
        "page": 114,
        "old": 34,
        "id": "A34-019",
        "source_ids": [
          "A34-019"
        ],
        "anchor": "A34-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Результат — аппаратный выбор с измерительным основанием. Отдельный хаб устанавливается открыто на столе и повторяет прежние тесты до переноса в корпус.",
        "y": 466.24249267578125,
        "page": 114,
        "old": 34,
        "id": "A34-020",
        "source_ids": [
          "A34-020"
        ],
        "anchor": "A34-020",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Этап 6. Самостоятельная станция E1 и выбранный корпус",
        "y": 514.0867309570312,
        "page": 114,
        "old": 34,
        "id": "A34-021",
        "source_ids": [
          "A34-021"
        ],
        "anchor": "A34-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для H2 собрать раму, габаритный макет и поддон; установить штатное питание, плату, кулер и вентиляторы. Сначала проверить открытый стенд, затем панели и фильтр. Антенны и микрофон должны оставаться доступными для перенастройки. При выборе готового H1 испытать его в штатном корпусе; изготовление куба в этом случае является отдельной веткой проекта.",
        "y": 541.1043701171875,
        "page": 114,
        "old": 34,
        "id": "A34-022",
        "source_ids": [
          "A34-022"
        ],
        "anchor": "A34-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Результат — исправная вентиляция, доступ к обслуживанию, отсутствие проблем радио и сохранение задержек. Декоративное покрытие и окончательная сетка ставятся после технической проверки, затем тест повторяется.",
        "y": 597.5332641601562,
        "page": 114,
        "old": 34,
        "id": "A34-023",
        "source_ids": [
          "A34-023"
        ],
        "anchor": "A34-023",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Этап 7. Обновление и удалённый доступ",
        "y": 661.3524780273438,
        "page": 114,
        "old": 34,
        "id": "A34-024",
        "source_ids": [
          "A34-024"
        ],
        "anchor": "A34-024",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Проверить локальную установку подписанного пакета, отказ подписи и откат. Настроить первый WireGuard-путь и отдельный SSH. Отключить VPS и убедиться, что кабинет остаётся управляемым локально.",
        "y": 688.3700561523438,
        "page": 114,
        "old": 34,
        "id": "A34-025",
        "source_ids": [
          "A34-025"
        ],
        "anchor": "A34-025",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Результат — станция обслуживается без разборки, а владелец имеет план восстановления без внешнего сайта. Ключи и backup не хранятся только на одном устройстве.",
        "y": 744.7990112304688,
        "page": 114,
        "old": 34,
        "id": "A34-026",
        "source_ids": [
          "A34-026"
        ],
        "anchor": "A34-026",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Этап 8. Второй экземпляр E2",
        "y": 56.314456939697266,
        "page": 115,
        "old": 34,
        "id": "A34-027",
        "source_ids": [
          "A34-027"
        ],
        "anchor": "A34-027",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Повторить сборку по ведомости и инструкции, желательно с участием другого человека. Различия в комплектующих записываются. Если требуется устное пояснение, оно переносится в схему или шаг проверки. Только такой повтор показывает, что инженерная основа действительно готова к нескольким первым экземплярам.",
        "y": 83.33208465576172,
        "page": 115,
        "old": 34,
        "id": "A34-028",
        "source_ids": [
          "A34-028"
        ],
        "anchor": "A34-028",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Gate",
            "Что должно быть доказано",
            "Что нельзя переносить дальше"
          ],
          [
            "Электрика",
            "Питание, уровни, стартовые состояния",
            "Случайные перезапуски и горячие соединения"
          ],
          [
            "Протокол",
            "Повторы, сроки, восстановление",
            "Неизвестная семантика успеха"
          ],
          [
            "Физика",
            "Свежесть, размещение, обратная связь",
            "Ложный световой цикл и незаметный уход в UNKNOWN"
          ],
          [
            "Корпус",
            "Температура, радио, обслуживание",
            "Перегрев, экранирование и закрытые разъёмы"
          ],
          [
            "Эксплуатация",
            "Обновление, backup, отзыв",
            "Единственная копия ключей и ручные недокументированные правки"
          ]
        ],
        "y": 157.84,
        "page": 115,
        "old": 34,
        "id": "A34-029",
        "source_ids": [
          "A34-029"
        ],
        "anchor": "A34-029",
        "content_status": "active"
      },
      {
        "kind": "h3",
        "text": "Что является началом изготовления",
        "y": 396.3853454589844,
        "page": 115,
        "old": 34,
        "id": "A34-030",
        "source_ids": [
          "A34-030"
        ],
        "anchor": "A34-030",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Разметка и сверление выполняются по согласованной аппаратной ревизии, а не по иллюстрации из книги. Атлас даёт исходные размеры и расчёты; комплект рабочего выпуска дополняется фактическими CAD, распиновками купленных модулей и результатами стенда.",
        "y": 414.8465576171875,
        "page": 115,
        "old": 34,
        "id": "A34-031",
        "source_ids": [
          "A34-031"
        ],
        "anchor": "A34-031",
        "content_status": "active"
      },
      {
        "kind": "lead",
        "text": "Первый этап связывает достоверные наблюдения с малым замкнутым циклом. Обучение более сложной модели начинается после проверки этого основания.",
        "id": "I24-001",
        "old": "I24",
        "source_ids": [
          "I24-001"
        ],
        "anchor": "I24-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Исследовательская ветка: от наблюдения к допуску модели",
        "id": "I24-002",
        "old": "I24",
        "source_ids": [
          "I24-002"
        ],
        "anchor": "I24-002",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Шаг",
            "Рабочий результат",
            "Условие перехода"
          ],
          [
            "1. Наблюдение",
            "Каналы света, температуры, присутствия с качеством и временем",
            "Потеря данных видна; измерение не подменяется кешем"
          ],
          [
            "2. Объекты",
            "Карта кабинета, связь реле с лампой, профиль ручного режима",
            "Нет неоднозначных нагрузок и единиц"
          ],
          [
            "3. Эталон",
            "Конечный автомат, журнал, физическое подтверждение",
            "Проходит обязательные сценарии отказа"
          ],
          [
            "4. Модель",
            "Оценка вклада света и состояния присутствия",
            "Проверена на отложенных эпизодах"
          ],
          [
            "5. AI в тени",
            "Типизированные предложения и сравнение с эталоном",
            "Измерен выигрыш без ухудшения критических ошибок"
          ],
          [
            "6. Ограниченный допуск",
            "Один пакет процесса с возможностью отката",
            "Пользователь понимает пределы; защита независима"
          ]
        ],
        "id": "I24-003",
        "old": "I24",
        "source_ids": [
          "I24-003"
        ],
        "anchor": "I24-003",
        "content_status": "active"
      },
      {
        "kind": "figure",
        "figure": "I24",
        "caption": "Развитие по доказанным возможностям, а не по календарным обещаниям. Ветку видео/JEPA можно исследовать параллельно, не задерживая работающий кабинет.",
        "id": "I24-004",
        "old": "I24",
        "source_ids": [
          "I24-004"
        ],
        "anchor": "I24-004",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Что зафиксировать до программирования",
        "id": "I24-005",
        "old": "I24",
        "source_ids": [
          "I24-005"
        ],
        "anchor": "I24-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Нужны схемы логических объектов, контракт наблюдения, контракт предложения, таблица ошибок, семантика времени и список инвариантов. В архиве приложены примеры JSON и проверяемая учебная схема; они не объявлены окончательной бинарной спецификацией KQIP.",
        "id": "I24-006",
        "old": "I24",
        "source_ids": [
          "I24-006"
        ],
        "anchor": "I24-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для первой модели фиксируем условия сбора, разделение эпизодов и эталон сравнения. Аппаратуру собираем по монтажным картам главы 16 и процедуре первого включения. Исследование модели не требует перепаять рабочий узел ради нового программного названия.",
        "id": "I24-007",
        "old": "I24",
        "edited": true,
        "source_ids": [
          "I24-007"
        ],
        "anchor": "I24-007",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Что сознательно не включать в первый runtime",
        "id": "I24-008",
        "old": "I24",
        "source_ids": [
          "I24-008"
        ],
        "anchor": "I24-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Первый runtime сохраняет ограниченный набор полномочий. Обязательный видеопоток, автономный online-RL на исполнительных устройствах, универсальное выполнение shell-команд, распределённый консенсус хабов, обучаемое отключение защит и магазин произвольных плагинов в него не входят. Каждое из этих расширений требует отдельной задачи и проверки риска после базового сценария.",
        "id": "I24-009",
        "old": "I24",
        "source_ids": [
          "I24-009"
        ],
        "anchor": "I24-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Не требуется и собственное ядро Linux. Собственная часть проекта — физические контракты, модель мира, управление процессами и исполнение. Разработка драйвера оправдана реальным устройством без подходящей поддержки, а не желанием назвать весь стек авторским.",
        "id": "I24-010",
        "old": "I24",
        "source_ids": [
          "I24-010"
        ],
        "anchor": "I24-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Пять исследовательских гипотез",
        "id": "I24-011",
        "old": "I24",
        "source_ids": [
          "I24-011"
        ],
        "anchor": "I24-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Переносимость. Пакет света связывается со второй комнатой без изменения ядра; измеряем число ручных операций и время настройки.",
        "id": "I24-012",
        "old": "I24",
        "source_ids": [
          "I24-012"
        ],
        "anchor": "I24-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Меньше ошибок. Гибридный оценщик уменьшает ложные выключения относительно согласованного эталона на независимых эпизодах; отслеживаем цену дополнительных включений.",
        "id": "I24-013",
        "old": "I24",
        "source_ids": [
          "I24-013"
        ],
        "anchor": "I24-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Полезное самоизмерение. Активный запрос данных сокращает неопределённые решения при ограниченной дополнительной энергии и трафике.",
        "id": "I24-014",
        "old": "I24",
        "source_ids": [
          "I24-014"
        ],
        "anchor": "I24-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Независимость от AI. Отказ worker не нарушает сроков базового цикла и не теряет ручное управление.",
        "id": "I24-015",
        "old": "I24",
        "source_ids": [
          "I24-015"
        ],
        "anchor": "I24-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Понятность. Пользователь по карточке решения верно объясняет, что известно и что ещё не подтверждено. Это отдельная проверка интерфейса, не эстетическая оценка.",
        "id": "I24-016",
        "old": "I24",
        "source_ids": [
          "I24-016"
        ],
        "anchor": "I24-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Первое практическое занятие",
        "id": "I24-017",
        "old": "I24",
        "source_ids": [
          "I24-017"
        ],
        "anchor": "I24-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Не покупать GPU. Запустить прилагаемую симуляцию, изучить CSV и изменить вклад лампы. Затем подготовить сборку сенсорного и релейного узла по монтажным картам главы 16, без опасной нагрузки. Первое программное действие — записать полноценное наблюдение и показать его свежесть. Первое физическое действие — безопасно включить лампу вручную и проверить, какие каналы действительно меняются.",
        "id": "I24-018",
        "old": "I24",
        "edited": true,
        "source_ids": [
          "I24-018"
        ],
        "anchor": "I24-018",
        "content_status": "active"
      },
      {
        "kind": "quote",
        "text": "Готовность первого экземпляра подтверждает воспроизводимый цикл наблюдения, решения, ограничения и проверки. На этой основе можно оценить пользу следующего AI-расширения.",
        "id": "I24-019",
        "old": "I24",
        "source_ids": [
          "I24-019"
        ],
        "anchor": "I24-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Начинаем с измерительного и исполнительного стенда, фиксируем контракты, собираем собственные эпизоды и проверяем эталон. Новая AI-модель получает место в системе только после такого сравнения; красивое название не даёт ей права на физическое действие.",
        "id": "I27-011",
        "old": "I27",
        "edited": true,
        "source_ids": [
          "I27-011"
        ],
        "anchor": "I27-011",
        "content_status": "active"
      },
      {
        "id": "R48-001",
        "kind": "h2",
        "text": "Первый маршрут с условиями остановки",
        "source_ids": [
          "R48-001"
        ],
        "anchor": "R48-001",
        "content_status": "active"
      },
      {
        "id": "R48-002",
        "kind": "table",
        "rows": [
          [
            "Этап",
            "Результат",
            "Стоп-условие"
          ],
          [
            "0. Контракты",
            "Референсные трассы command/time/manual/restore",
            "Стороны по-разному трактуют одно событие"
          ],
          [
            "1. MCU-совместимость",
            "Зафиксированные SDK/TLS/BLE/OTA и измеренные ресурсы",
            "Нет безопасного rollback или кнопка блокируется"
          ],
          [
            "2. Точный E0",
            "Одна BOM, низковольтная нагрузка, карта питания",
            "Неизвестный IN/VIN, КЗ, импульс при старте"
          ],
          [
            "3. Ограниченное управление",
            "PIR-only, ручной режим, неизменяемые команды",
            "Необъяснимое действие или нарушение ручного OFF"
          ],
          [
            "4. KMP и восстановление",
            "Два телефона, отзыв, смена сети, резервная копия",
            "Старая станция/сессия получает прежние права"
          ],
          [
            "5. E1 и исследование",
            "Корпус по замерам, голос, квалифицированное присутствие",
            "Расширение ухудшает проверенный базовый контур"
          ]
        ],
        "source_ids": [
          "R48-002"
        ],
        "anchor": "R48-002",
        "content_status": "active"
      },
      {
        "id": "R48-003",
        "kind": "p",
        "text": "Одновременно ведётся одна рискованная аппаратная ветка и одна связанная программная проверка; новые ML-эксперименты не блокируют завершение E0. Трудоёмкость фиксируется по фактическим опытам и пересматривается после этапа 1. Из количества глав нельзя вывести достоверные сроки одного автора. Учебная ценность широкого проекта сохраняется, но условия первого успеха остаются узкими.",
        "source_ids": [
          "R48-003"
        ],
        "anchor": "R48-003",
        "content_status": "active"
      }
    ],
    "origins": [
      "A34",
      "I24"
    ],
    "question": "Что должно быть готово после каждого шага?",
    "intro": "Сборка движется короткими законченными циклами. Один датчик стабильно выдаёт наблюдение. Один исполнитель реагирует и сохраняет кнопку без сети. Затем станция связывает их в правило, а приложение объясняет результат.\n\nВторой экземпляр проверяет не только электронику, но и полноту нашей инструкции. Если для его запуска требуется помнить незаписанную правку первого стенда, проект ещё не воспроизводится. Эти скрытые зависимости нужно устранить до дальнейшего расширения.",
    "id": "ch-48",
    "slug": "chapter-48"
  },
  {
    "number": 49,
    "title": "Проверка проекта на прочность",
    "group": "Инженерные компромиссы",
    "units": [
      {
        "kind": "lead",
        "text": "Конструкция объединяет требования, которые ограничивают друг друга. Ниже разобраны шесть компромиссов и способы проверить выбранные решения на стенде.",
        "y": 168.675537109375,
        "page": 116,
        "old": 35,
        "id": "A35-001",
        "edited": true,
        "source_ids": [
          "A35-001"
        ],
        "anchor": "A35-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Здесь система рассматривается с позиций аппаратуры, системного ПО, безопасности, акустики, интерфейса и эксплуатации. Выводы описывают инженерную позицию проекта и открытые проверки. Агентное ревью помогает сопоставлять аргументы; лабораторные и аппаратные испытания требуют самостоятельных результатов.",
        "y": 219.78143310546875,
        "page": 116,
        "old": 35,
        "id": "A35-002",
        "edited": true,
        "source_ids": [
          "A35-002"
        ],
        "anchor": "A35-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Конфликт 1. Вычислитель и механика",
        "y": 315.55059814453125,
        "page": 116,
        "old": 35,
        "id": "A35-003",
        "source_ids": [
          "A35-003"
        ],
        "anchor": "A35-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Выбор состоит в следующем: сразу купить мощную платформу с запасом или сначала ограничиться малой платой. Первый подход ускоряет эксперименты с голосом, второй уменьшает корпус и питание, но может рано упереться в speech-нагрузку.",
        "y": 342.5682373046875,
        "page": 116,
        "old": 35,
        "id": "A35-004",
        "source_ids": [
          "A35-004"
        ],
        "anchor": "A35-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Решение: H0 на имеющемся ПК, затем измерительный выбор автономной основы. Mini-ITX сохраняется как прозрачная конструкция куба, мини-ПК — как быстрый альтернативный экземпляр. Конкретная модель CPU не считается окончательной до RU/ EN-бенчмарка.",
        "y": 398.9971618652344,
        "page": 116,
        "old": 35,
        "id": "A35-005",
        "source_ids": [
          "A35-005"
        ],
        "anchor": "A35-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Инженерное следствие: размеры корпуса выделены в параметрическую компоновку; реальные сокет, I/O и кабели вынесены в обязательные проверки. Ведомость профиля привязана к явной схеме стыковки.",
        "y": 471.4010925292969,
        "page": 116,
        "old": 35,
        "id": "A35-006",
        "source_ids": [
          "A35-006"
        ],
        "anchor": "A35-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Незакрытый риск: собственный металлический корпус ещё не проверен физически. Условие перехода — полноразмерный макет, проверка доступа и тепловой стенд.",
        "y": 527.8300170898438,
        "page": 116,
        "old": 35,
        "id": "A35-007",
        "source_ids": [
          "A35-007"
        ],
        "anchor": "A35-007",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Конфликт 2. Воздух, пыль и голос",
        "y": 575.6742553710938,
        "page": 116,
        "old": 35,
        "id": "A35-008",
        "source_ids": [
          "A35-008"
        ],
        "anchor": "A35-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Выбор состоит в следующем: три вентилятора дают простой запас охлаждения, но усложняют акустику. Reverse-blade соответствует визуальной идее, но не является технической необходимостью.",
        "y": 602.69189453125,
        "page": 116,
        "old": 35,
        "id": "A35-009",
        "source_ids": [
          "A35-009"
        ],
        "anchor": "A35-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Решение: двухприточная/одновытяжная схема остаётся исходной, а количество и обороты подтверждаются измерением. CPU сохраняет отдельный кулер. Микрофон сначала выносной; reverse-blade — опция конкретной модели.",
        "y": 659.1207885742188,
        "page": 116,
        "old": 35,
        "id": "A35-010",
        "source_ids": [
          "A35-010"
        ],
        "anchor": "A35-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Инженерное следствие: нужны рабочая точка вентилятора, оценка сопротивления фильтра, отдельный тракт БП и проверка голоса при длительной нагрузке. Предположение «два на вход автоматически дают положительное давление» не принимается.",
        "y": 715.5497436523438,
        "page": 116,
        "old": 35,
        "id": "A35-011",
        "source_ids": [
          "A35-011"
        ],
        "anchor": "A35-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Условие перехода: температуры всех узлов, загрязнённый фильтр, отсутствие рециркуляции и сравнение качества команд.",
        "y": 57.42815399169922,
        "page": 117,
        "old": 35,
        "id": "A35-012",
        "source_ids": [
          "A35-012"
        ],
        "anchor": "A35-012",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Конфликт 3. Электроника и физические данные",
        "y": 105.27232360839844,
        "page": 117,
        "old": 35,
        "id": "A35-013",
        "source_ids": [
          "A35-013"
        ],
        "anchor": "A35-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Выбор состоит в следующем: объединить все датчики в красивый куб или разделить измерительные точки. Объединение упрощает корпус, но делает температуру и присутствие плохо связанными с комнатой.",
        "y": 132.28997802734375,
        "page": 117,
        "old": 35,
        "id": "A35-014",
        "source_ids": [
          "A35-014"
        ],
        "anchor": "A35-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Решение: Sense и Presence отдельные. У температуры и lux допускаются разные точки даже внутри одной логической комнаты. Первый исполнитель низковольтный и имеет физическую кнопку.",
        "y": 188.71890258789062,
        "page": 117,
        "old": 35,
        "id": "A35-015",
        "source_ids": [
          "A35-015"
        ],
        "anchor": "A35-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Инженерное следствие: необходимы карта GPIO, расчёт I²C, различие микросхемы и готового модуля, проверка самовозбуждения света и несколько уровней подтверждения действия.",
        "y": 245.14785766601562,
        "page": 117,
        "old": 35,
        "id": "A35-016",
        "source_ids": [
          "A35-016"
        ],
        "anchor": "A35-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Условие перехода: повторяемые измерения, отсутствие стартового импульса реле и проверка присутствия при неподвижном человеке.",
        "y": 285.6017761230469,
        "page": 117,
        "old": 35,
        "id": "A35-017",
        "source_ids": [
          "A35-017"
        ],
        "anchor": "A35-017",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Конфликт 4. ПО, протокол и доверие",
        "y": 333.4459533691406,
        "page": 117,
        "old": 35,
        "id": "A35-018",
        "source_ids": [
          "A35-018"
        ],
        "anchor": "A35-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Выбор состоит в следующем: полностью Rust-стек против зрелых компонентов с C/FFI; универсальный собственный транспорт против малого проверяемого профиля.",
        "y": 360.46356201171875,
        "page": 117,
        "old": 35,
        "id": "A35-019",
        "source_ids": [
          "A35-019"
        ],
        "anchor": "A35-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Решение: собственная предметная логика и протокол на Rust, но Linux, TLS, SQL и ML-runtime не переписываются ради языковой чистоты. Первый KQIP — TCP/TLS 1.3 и ограниченный CBOR. Сопряжение устройств опирается на готовый защищённый механизм, Linux-bootstrap выделен отдельно.",
        "y": 400.91754150390625,
        "page": 117,
        "old": 35,
        "id": "A35-020",
        "source_ids": [
          "A35-020"
        ],
        "anchor": "A35-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Инженерное следствие: ACCEPTED отделяется от APPLIED; используются outbox, controller_fence, ограничения парсера и проверка реальной TLS-конфигурации ESP-IDF. Восстановление времени не использует trustAll.",
        "y": 473.32147216796875,
        "page": 117,
        "old": 35,
        "id": "A35-021",
        "source_ids": [
          "A35-021"
        ],
        "anchor": "A35-021",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Условие перехода: interop Core/MCU/KMP, повторы, fuzz, отзыв и незавершённое сопряжение.",
        "y": 529.7504272460938,
        "page": 117,
        "old": 35,
        "id": "A35-022",
        "source_ids": [
          "A35-022"
        ],
        "anchor": "A35-022",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Конфликт 5. Интеграция в обычную жизнь",
        "y": 561.61962890625,
        "page": 117,
        "old": 35,
        "id": "A35-023",
        "source_ids": [
          "A35-023"
        ],
        "anchor": "A35-023",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Выбор состоит в следующем: максимально автономная собственная Wi-Fi-сеть против удобства пользователя и одного радиомодуля. Полностью бесшовный удалённый доступ против прозрачной первой реализации.",
        "y": 588.63720703125,
        "page": 117,
        "old": 35,
        "id": "A35-024",
        "source_ids": [
          "A35-024"
        ],
        "anchor": "A35-024",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Решение: домашний Wi-Fi — основной путь, Ethernet дополнительный. Отдельная сеть устройств возможна позже без переключения телефона. E0 remote использует WireGuard; встроенный relay/VPN проходит отдельный мобильный gate.",
        "y": 645.066162109375,
        "page": 117,
        "old": 35,
        "id": "A35-025",
        "source_ids": [
          "A35-025"
        ],
        "anchor": "A35-025",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Инженерное следствие: проверяются изоляция клиентов роутером, смена сети, разрешения iOS/Android и раздельные полномочия пользователя и SSH. Удалённая доступность не объявляется гарантированной при любых блокировках.",
        "y": 701.4950561523438,
        "page": 117,
        "old": 35,
        "id": "A35-026",
        "source_ids": [
          "A35-026"
        ],
        "anchor": "A35-026",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Условие перехода: второй человек подключает узел, восстанавливает сеть и понимает неопределённый результат команды.",
        "y": 57.42815399169922,
        "page": 118,
        "old": 35,
        "id": "A35-027",
        "source_ids": [
          "A35-027"
        ],
        "anchor": "A35-027",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Конфликт 6. Восстановление и повторная сборка",
        "y": 105.27232360839844,
        "page": 118,
        "old": 35,
        "id": "A35-028",
        "source_ids": [
          "A35-028"
        ],
        "anchor": "A35-028",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Выбор состоит в следующем: быстрое обновление вручную против воспроизводимого состояния; простое копирование диска против риска двух контроллеров.",
        "y": 132.28997802734375,
        "page": 118,
        "old": 35,
        "id": "A35-029",
        "source_ids": [
          "A35-029"
        ],
        "anchor": "A35-029",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Решение: подпись и штатная процедура установки, A/B для appliance-профиля, совместимые миграции, восстановление с изоляцией старой станции и ротацией полномочий.",
        "y": 172.743896484375,
        "page": 118,
        "old": 35,
        "id": "A35-030",
        "source_ids": [
          "A35-030"
        ],
        "anchor": "A35-030",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Инженерное следствие: backup включает состояние доверия; модель речи рассматривается как версия поведения; изготовление E2 проверяет полноту инструкции, а не только успех автора.",
        "y": 229.17282104492188,
        "page": 118,
        "old": 35,
        "id": "A35-031",
        "source_ids": [
          "A35-031"
        ],
        "anchor": "A35-031",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Условие перехода: прерванное обновление, старый архив, заполнение диска и второй экземпляр без скрытых ручных правок.",
        "y": 285.6017761230469,
        "page": 118,
        "old": 35,
        "id": "A35-032",
        "source_ids": [
          "A35-032"
        ],
        "anchor": "A35-032",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Принятые решения и условия проверки",
        "y": 333.4459533691406,
        "page": 118,
        "old": 35,
        "id": "A35-033",
        "source_ids": [
          "A35-033"
        ],
        "anchor": "A35-033",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Зафиксированы архитектурные границы, основной сетевой путь, низковольтный первый исполнитель, схема собственного протокола, форма наблюдения и маршрут сборки. Открыты точная compute-конфигурация, окончательная CAD-компоновка, Linux BLE-onboarding E1, встроенный аудиомодуль и численные эксплуатационные показатели. Это конкретные инженерные задачи с проверками, а не скрытые обещания готовности.",
        "y": 360.46356201171875,
        "page": 118,
        "old": 35,
        "id": "A35-034",
        "source_ids": [
          "A35-034"
        ],
        "anchor": "A35-034",
        "content_status": "active"
      },
      {
        "kind": "lead",
        "text": "Проверим ту же систему со стороны измерений, статистики и управления. Эти вопросы дополняют конструктивные компромиссы: даже исправное железо может принять неверное решение из хороших на вид данных.",
        "id": "I25-001",
        "old": "I25",
        "edited": true,
        "source_ids": [
          "I25-001"
        ],
        "anchor": "I25-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "1. Измерительная инженерия",
        "id": "I25-002",
        "old": "I25",
        "source_ids": [
          "I25-002"
        ],
        "anchor": "I25-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Возражение. Один аккуратный числовой поток скрывает размещение, задержку, насыщение и собственный нагрев. Дорогой датчик ошибочно воспринимается как точная картина комнаты.",
        "id": "I25-003",
        "old": "I25",
        "source_ids": [
          "I25-003"
        ],
        "anchor": "I25-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Контракт содержит объект, интервал измерения, ревизию размещения, качество, калибровку и явные пробелы. Комнатные сенсоры отделены от корпуса станции. Влияние лампы определяется на конкретной плоскости. Проверка — эпизоды с переносом и внешними помехами.",
        "id": "I25-004",
        "old": "I25",
        "source_ids": [
          "I25-004"
        ],
        "anchor": "I25-004",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "2. Модели и статистика",
        "id": "I25-005",
        "old": "I25",
        "source_ids": [
          "I25-005"
        ],
        "anchor": "I25-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Возражение. Успешный прогноз ошибочно принимается за причинное понимание; соседние отсчёты создают утечку теста; confidence путается с безопасностью.",
        "id": "I25-006",
        "old": "I25",
        "source_ids": [
          "I25-006"
        ],
        "anchor": "I25-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Разделены наблюдение, оценка, прогноз и действие. Введены эпизодные разбиения, журнал воздействия и проверка области применимости. Jev и JEPA разведены по роли. Перестановка вариантов, новые условия и цена ошибок вошли в тесты.",
        "id": "I25-007",
        "old": "I25",
        "source_ids": [
          "I25-007"
        ],
        "anchor": "I25-007",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "3. Управление и физическое исполнение",
        "id": "I25-008",
        "old": "I25",
        "source_ids": [
          "I25-008"
        ],
        "anchor": "I25-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Возражение. Мягкий штраф может позволить нежелательное действие; старый план перезапишет ручную кнопку; подтверждение прошивки выдаётся за достижение цели.",
        "id": "I25-009",
        "old": "I25",
        "source_ids": [
          "I25-009"
        ],
        "anchor": "I25-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Раздельные ограничения, ручной режим с собственной ревизией, повторная проверка предусловий и несколько видов результата ограничивают исполнение. Бинарному реле нельзя приписывать возможности непрерывного регулятора. Состояние «исход неизвестен» остаётся частью API.",
        "id": "I25-010",
        "old": "I25",
        "source_ids": [
          "I25-010"
        ],
        "anchor": "I25-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "4. Rust, безопасность и ресурсы",
        "id": "I25-011",
        "old": "I25",
        "source_ids": [
          "I25-011"
        ],
        "anchor": "I25-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Возражение. Один асинхронный процесс может зависнуть на native-инференсе; файл модели меняет поведение без смены кода; Rust ошибочно воспринимается как полная защита всех зависимостей.",
        "id": "I25-012",
        "old": "I25",
        "source_ids": [
          "I25-012"
        ],
        "anchor": "I25-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "AI-worker вынесен из быстрого пути. У артефакта есть полный манифест и отдельный допуск. Ограничены IPC, память и предложения. Критические права и секреты не попадают в модель. Cgroups рассматривается как средство управления ресурсами, не доказательство hard real-time.",
        "id": "I25-013",
        "old": "I25",
        "source_ids": [
          "I25-013"
        ],
        "anchor": "I25-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "5. Сквозной сценарий и критика «революционности»",
        "id": "I25-014",
        "old": "I25",
        "source_ids": [
          "I25-014"
        ],
        "anchor": "I25-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Возражение. Архитектура может остаться набором громких названий без преимущества перед автоматом. Добавление Jev создаст внешнюю зависимость, а новая камера — лишнюю приватную телеметрию.",
        "id": "I25-015",
        "old": "I25",
        "source_ids": [
          "I25-015"
        ],
        "anchor": "I25-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Базовый профиль не требует камеры и облачной модели. Есть воспроизводимый числовой пример, сильный эталон для будущего сравнения и пять фальсифицируемых гипотез. Новизна сформулирована как направление, а не доказанный мировой приоритет.",
        "id": "I25-016",
        "old": "I25",
        "source_ids": [
          "I25-016"
        ],
        "anchor": "I25-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "6. Совместимость и редакционное ревью",
        "id": "I25-017",
        "old": "I25",
        "source_ids": [
          "I25-017"
        ],
        "anchor": "I25-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Проблема. Удобное объяснение может скрыть критические детали: модель времени, стартовый импульс реле, единицы измерения, ограничение памяти. И наоборот, подробная схема бесполезна, если непонятно, что именно она должна обеспечить.",
        "id": "I25-018",
        "old": "I25",
        "edited": true,
        "source_ids": [
          "I25-018"
        ],
        "anchor": "I25-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Связываем объяснение с проверкой: что измерить, какой результат ожидать, когда остановиться. Схемы различают физические соединения и логические связи; числа симуляции не выдаются за измерения оборудования. Размерные, электрические и программные примеры читаются вместе с их исходными условиями.",
        "id": "I25-019",
        "old": "I25",
        "edited": true,
        "source_ids": [
          "I25-019"
        ],
        "anchor": "I25-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Оставшиеся вопросы для стенда Реальные задержки и тепловой режим; качество присутствия в кабинете; допустимые пороги и паузы; эффективность локальной RU/EN-модели; совместимость выбранного ML-backend; точность часов; устойчивость восстановленного управления. Эти пункты не закрываются редактурой документа или аналитическим ревью.",
        "id": "I25-020",
        "old": "I25",
        "source_ids": [
          "I25-020"
        ],
        "anchor": "I25-020",
        "content_status": "active"
      },
      {
        "id": "R49-001",
        "kind": "h2",
        "text": "Реестр рисков первого экземпляра",
        "source_ids": [
          "R49-001"
        ],
        "anchor": "R49-001",
        "content_status": "active"
      },
      {
        "id": "R49-002",
        "kind": "p",
        "text": "Это не статистика отказов, а начальная инженерная оценка. Вероятность и влияние: 1 — низкие, 2 — средние, 3 — высокие; произведение служит только порядком работы. Владелец всех действий сейчас — автор проекта в указанной роли. «Проектно решён» не равно «проверен на стенде». Полный реестр, связи с замечаниями и картами находятся в review_r2/risk_register.csv.",
        "source_ids": [
          "R49-002"
        ],
        "anchor": "R49-002",
        "content_status": "active"
      },
      {
        "id": "R49-003",
        "kind": "table",
        "rows": [
          [
            "Риск / P×I",
            "Триггер и действие",
            "Статус / карта"
          ],
          [
            "Повтор после кнопки / 3×3",
            "ACK потерян; дедуп до предусловий, high-water",
            "Проектно решён; Q-01"
          ],
          [
            "Старый срок после сна / 3×3",
            "Resume/reboot; BOOTTIME, консервативный дедлайн",
            "Проектно решён; T-01"
          ],
          [
            "Клон старого Hub / 2×3",
            "Restore; новый ключ, owner transfer, passive start",
            "Нужен стенд; S-01"
          ],
          [
            "MCU TLS/BLE/OTA не сходятся / 3×3",
            "Heap, handshake или rollback не проходят",
            "Открыт; MCU-01"
          ],
          [
            "Реле/питание не совпали с картой / 2×3",
            "Неизвестный IN, пуск или brownout",
            "Открыт до BOM; E-01"
          ],
          [
            "PIR приняли за отсутствие / 3×2",
            "Неподвижный человек; запрет авто-OFF E0",
            "Проектно решён; F-01"
          ],
          [
            "Слишком широкий scope / 3×2",
            "Новый стек блокирует первый свет",
            "Контроль этапов; PLAN-01"
          ],
          [
            "Диск или retention ломают обещание / 2×3",
            "ENOSPC, потеря оснований; запрет ACCEPTED",
            "Нужен стенд; D-01"
          ]
        ],
        "source_ids": [
          "R49-003"
        ],
        "anchor": "R49-003",
        "content_status": "active"
      },
      {
        "id": "R49-004",
        "kind": "table",
        "rows": [
          [
            "Риск / P×I",
            "Триггер и действие",
            "Статус / карта"
          ],
          [
            "Привязка KMP/FFI / 3×2",
            "Отмена/callback ломаются на iOS",
            "Открыт; APP-01"
          ],
          [
            "Голос выполняет чужую фразу / 2×3",
            "ТВ, отрицание; ограниченный субъект",
            "Открыт E1; V-01"
          ],
          [
            "Корпус мешает радио/охлаждению / 2×2",
            "Закрытые панели ухудшают измерения",
            "Открыт H2; M/H/N-01"
          ],
          [
            "Зависимость от облачного AI / 2×2",
            "Нет сервиса или лицензии; локальный baseline",
            "Исключена из E0; AI-01"
          ],
          [
            "Неверная ML-валидация / 3×2",
            "Утечка времени/контрфактическая метрика",
            "Проектно решён; ML-01"
          ],
          [
            "Компрометация зависимостей / 2×3",
            "Advisory, новый источник/веса",
            "Постоянный; SUP-01"
          ],
          [
            "Обновление возвращает старое доверие / 2×3",
            "Rollback схемы/ключей",
            "Нужен стенд; R/S-01"
          ],
          [
            "Материалы, контрафакт, EOL / 2×2",
            "Другая ревизия/неизвестный пластик",
            "Проверка экземпляра; BOM-01"
          ]
        ],
        "source_ids": [
          "R49-004"
        ],
        "anchor": "R49-004",
        "content_status": "active"
      },
      {
        "id": "R49-005",
        "kind": "p",
        "text": "Профиль DEV допускает физическую отладку и не защищает секреты от владельца лабораторной платы. APPLIANCE требует отдельной квалификации защиты. Личные эксперименты, передача сборки для совместного теста и выпуск на рынок — разные основания ответственности; книга не является сертификатом электрической, радио- или коммерческой пригодности.",
        "source_ids": [
          "R49-005"
        ],
        "anchor": "R49-005",
        "content_status": "active"
      },
      {
        "id": "R49-006",
        "kind": "p",
        "text": "Ограничитель расширения: до закрытия команд, времени, ручного режима и физического выхода не добавляем обучение управляющей политики как обязательную зависимость. Это не отказ от физического интеллекта, а способ получить эксперимент, которому можно доверять. Энергия станции, время настройки и ручные исправления измеряются вместе с пользой алгоритма.",
        "source_ids": [
          "R49-006"
        ],
        "anchor": "R49-006",
        "content_status": "active"
      }
    ],
    "origins": [
      "A35",
      "I25"
    ],
    "question": "Где решения проекта конфликтуют друг с другом?",
    "intro": "Качество нельзя получить независимым максимумом характеристик. Более плотный фильтр лучше задерживает часть пыли, но меняет воздушный тракт. Более тяжёлая модель может повысить качество отдельной задачи, но занять память и задержать ответ. Удобное восстановление может ослабить границу доверия.\n\nНиже собраны такие конфликты и условия, при которых принятое решение следует пересмотреть. Это не история переписки и не протокол внешней комиссии, а карта аргументов для собственной инженерной проверки.",
    "id": "ch-49",
    "slug": "chapter-49"
  },
  {
    "number": 50,
    "title": "Рабочие карты и словарь",
    "group": "Практика",
    "units": [
      {
        "kind": "lead",
        "text": "Каждая карточка испытания связывает конфигурацию, наблюдение и решение о следующем шаге. Заполненные результаты позволяют воспроизвести проверку и оценить готовность системы.",
        "y": 168.675537109375,
        "page": 138,
        "old": 41,
        "id": "A41-001",
        "source_ids": [
          "A41-001"
        ],
        "anchor": "A41-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Единая шапка протокола",
        "y": 236.06771850585938,
        "page": 138,
        "old": 41,
        "id": "A41-002",
        "source_ids": [
          "A41-002"
        ],
        "anchor": "A41-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Для любой проверки записываются ID сборки, дата, версии железа и прошивки, схема подключения, используемый источник питания, температура помещения, измерительные приборы и исходное состояние. Итог — PASS, FAIL или INCONCLUSIVE с приложенными данными. «Не удалось воспроизвести» не превращается в PASS.",
        "y": 263.0853576660156,
        "page": 138,
        "old": 41,
        "id": "A41-003",
        "source_ids": [
          "A41-003"
        ],
        "anchor": "A41-003",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "M-01 · Механика и обслуживание",
        "y": 342.8795166015625,
        "page": 138,
        "old": 41,
        "id": "A41-004",
        "source_ids": [
          "A41-004"
        ],
        "anchor": "A41-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Проверить диагонали рамы, посадку панелей без напряжения, удержание плат, безопасные края и отсутствие металлической стружки. Извлечь и вернуть фильтр пять раз. Снять SSD и отсоединить плату в предусмотренной последовательности. Проверить, не натягиваются ли кабели, не падают ли винты внутрь, не касаются ли лопасти решётки при лёгкой деформации панели.",
        "y": 369.89715576171875,
        "page": 138,
        "old": 41,
        "id": "A41-005",
        "source_ids": [
          "A41-005"
        ],
        "anchor": "A41-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Результат содержит фотографии закрытого и открытого корпуса, список крепежа и отклонения размеров. При обнаружении перекоса исправляется рама или чертёж, а не применяется более сильная затяжка.",
        "y": 458.2760925292969,
        "page": 138,
        "old": 41,
        "id": "A41-006",
        "source_ids": [
          "A41-006"
        ],
        "anchor": "A41-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "E-01 · Электрика и холодный старт",
        "y": 522.0952758789062,
        "page": 138,
        "old": 41,
        "id": "A41-007",
        "source_ids": [
          "A41-007"
        ],
        "anchor": "A41-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Без исполнительной нагрузки выполнить серию включений, reset MCU, входа в загрузчик и потери Wi-Fi. Осциллографом или логическим анализатором наблюдать линию управления; отдельно контролировать фактическое состояние контакта безопасным способом. Отсутствие видимого вспыхивания лампы не доказывает отсутствия короткого импульса.",
        "y": 549.1129150390625,
        "page": 138,
        "old": 41,
        "id": "A41-008",
        "source_ids": [
          "A41-008"
        ],
        "anchor": "A41-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Затем подключить лампу 12 В, измерить ток, напряжение у узла и нагрев соединений при длительной работе. Проверить ручную кнопку без сервера. Фиксировать не только успешные циклы, но и min/max напряжения и причины reset.",
        "y": 637.4918823242188,
        "page": 138,
        "old": 41,
        "id": "A41-009",
        "source_ids": [
          "A41-009"
        ],
        "anchor": "A41-009",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "N-01 · Сеть и доверие",
        "y": 56.314456939697266,
        "page": 139,
        "old": 41,
        "id": "A41-010",
        "source_ids": [
          "A41-010"
        ],
        "anchor": "A41-010",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Проверить обычный домашний Wi-Fi, перезапуск роутера, другой адрес DHCP, гостевую сеть с изоляцией и неверный пароль. Приложение должно различать отсутствие сети, отсутствие станции и отказ аутентификации. После замены роутера не теряются ни владелец, ни идентичность устройств.",
        "y": 83.33208465576172,
        "page": 139,
        "old": 41,
        "id": "A41-011",
        "source_ids": [
          "A41-011"
        ],
        "anchor": "A41-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Отдельно проверить публичный PKI-профиль с неверным сроком, локальный pin-профиль с чужим ключом и неверной подписью, отозванного пользователя и старт без UTC. Проверка trustAll считается провалом. Недоступность VPS не должна останавливать локальный контур. Срок сертификата в pin-профиле не тестируется как скрытая универсальная настройка TLS.",
        "y": 155.73602294921875,
        "page": 139,
        "old": 41,
        "id": "A41-012",
        "source_ids": [
          "A41-012"
        ],
        "anchor": "A41-012",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "F-01 · Физическая логика",
        "y": 235.53021240234375,
        "page": 139,
        "old": 41,
        "id": "A41-013",
        "source_ids": [
          "A41-013"
        ],
        "anchor": "A41-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Использовать истории главы 44. В каждой сохранять исходные наблюдения, вычисленное состояние, активную политику, причину команды и её результат. Проверить, что запоздавшая история не превращается в живой вход, Unknown не равен Empty, а собственная лампа не создаёт цикл ON/OFF.",
        "y": 262.5478515625,
        "page": 139,
        "old": 41,
        "id": "A41-014",
        "edited": true,
        "source_ids": [
          "A41-014"
        ],
        "anchor": "A41-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Успех — не одно эффектное включение, а понятное поведение при дневном свете, неподвижном человеке, ручном вмешательстве и неисправном датчике. Новая физическая модель сначала работает в режиме наблюдения без исполнительных полномочий.",
        "y": 334.9517822265625,
        "page": 139,
        "old": 41,
        "id": "A41-015",
        "source_ids": [
          "A41-015"
        ],
        "anchor": "A41-015",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "V-01 · Голос RU/EN",
        "y": 414.7459411621094,
        "page": 139,
        "old": 41,
        "id": "A41-016",
        "source_ids": [
          "A41-016"
        ],
        "anchor": "A41-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Корпус фраз содержит разные формулировки, отрицания, названия похожих объектов и фоновые разговоры. Для каждого примера есть ожидаемое намерение, объект либо необходимость уточнения. Примеры: «включи основной свет», «освещение включи», «не включай подсветку», «turn on the desk light», «turn off the backlight».",
        "y": 441.76361083984375,
        "page": 139,
        "old": 41,
        "id": "A41-017",
        "source_ids": [
          "A41-017"
        ],
        "anchor": "A41-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Проверяются выносной микрофон, телефон, несколько расстояний и разные обороты вентиляторов. Отдельно фиксируются неверное действие, неверный объект, отказ распознавания и задержка. Случай, когда текст распознан неточно, но намерение верно, отличается от грамматически красивого текста с опасным действием.",
        "y": 514.1675415039062,
        "page": 139,
        "old": 41,
        "id": "A41-018",
        "source_ids": [
          "A41-018"
        ],
        "anchor": "A41-018",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "R-01 · Обновление и восстановление",
        "y": 593.9617309570312,
        "page": 139,
        "old": 41,
        "id": "A41-019",
        "source_ids": [
          "A41-019"
        ],
        "anchor": "A41-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "На тестовом накопителе прервать доставку пакета и запись неактивного раздела, запустить неисправный образ, заполнить раздел истории и восстановить архив. Проверить сохранность доверия, отсутствие двух активных контроллеров, совместимость схемы данных и возможность локальной диагностики без интернета.",
        "y": 620.9793701171875,
        "page": 139,
        "old": 41,
        "id": "A41-020",
        "source_ids": [
          "A41-020"
        ],
        "anchor": "A41-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Данные, которыми нельзя рисковать, предварительно выносятся из стенда. Отключение питания выполняется контролируемо, без вскрытия сетевого БП. Тест считается завершённым только после повторной работы физического сценария, а не после появления приглашения SSH.",
        "y": 693.38330078125,
        "page": 139,
        "old": 41,
        "id": "A41-021",
        "source_ids": [
          "A41-021"
        ],
        "anchor": "A41-021",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Краткий словарь",
        "y": 56.314456939697266,
        "page": 140,
        "old": 41,
        "id": "A41-022",
        "source_ids": [
          "A41-022"
        ],
        "anchor": "A41-022",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Термин",
            "Значение в Koobique"
          ],
          [
            "Capability",
            "Отдельная возможность устройства: измерять lux, принимать set(on), сообщать нажатие"
          ],
          [
            "Desired state",
            "Намерение получить состояние; ещё не подтверждённая физическая реальность"
          ],
          [
            "Reported state",
            "Состояние, которое сообщил узел; его смысл зависит от наличия обратной связи"
          ],
          [
            "Observation",
            "Измерение с источником, единицей, временем и качеством"
          ],
          [
            "Estimate",
            "Вычисленная оценка, например присутствие; не приравнивается к сырому измерению"
          ],
          [
            "Stale",
            "Данные слишком старые для текущего решения, даже если последнее значение выглядит правдоподобно"
          ],
          [
            "Boot ID",
            "Идентификатор загрузки, разделяющий эпохи монотонных часов устройства"
          ],
          [
            "Monotonic clock",
            "Шкала длительностей; Linux MONOTONIC не включает suspend, BOOTTIME включает; reboot задаёт новую привязку"
          ],
          [
            "Idempotency",
            "Свойство операции при повторе; не заменяет дедупликацию и защиту последующего ручного вмешательства"
          ],
          [
            "Outbox",
            "Запись для последующей доставки, созданная в той же транзакции, что принятие команды"
          ],
          [
            "Fencing",
            "Устройство принимает только назначенный владельцем controller key/fence; один счётчик не доказывает владение"
          ],
          [
            "ManualOverride / lease",
            "Ручной режим может быть бессрочным; lease — отдельный вариант с оговорённым сроком"
          ],
          [
            "Golden vector",
            "Зафиксированные байты и ожидаемый результат для проверки совместимости кодеков"
          ],
          [
            "Backpressure",
            "Ограничение входного потока, когда потребитель не успевает обработать данные"
          ],
          [
            "HIL",
            "Проверка программной системы с реальным железом в испытательном контуре"
          ],
          [
            "RTF",
            "Время обработки аудио, делённое на длительность записи; не вся задержка голосового ответа"
          ],
          [
            "VAD / AEC",
            "Обнаружение речи / подавление эха с учётом воспроизводимого сигнала"
          ],
          [
            "Pull-up",
            "Подтяжка линии к питанию через сопротивление; часть электрической схемы, не «ускоритель»"
          ]
        ],
        "y": 85.43,
        "page": 140,
        "old": 41,
        "id": "A41-023",
        "source_ids": [
          "A41-023"
        ],
        "anchor": "A41-023",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Термин",
            "Значение в Koobique"
          ],
          [
            "Open drain",
            "Выход, который активно тянет линию вниз; высокий уровень формируется отдельно"
          ],
          [
            "Return path",
            "Путь возвратного тока, влияющий на петлю сигнала, помехи и целостность фронта"
          ],
          [
            "Working point",
            "Реальные давление и расход вентилятора с данным трактом, а не максимальный CFM"
          ],
          [
            "A/B update",
            "Установка в неактивный системный раздел с пробным запуском и подтверждением"
          ],
          [
            "Secure boot",
            "Проверка разрешённой загрузочной цепочки; сама по себе не шифрует все данные"
          ],
          [
            "Local-first",
            "Основная логика и данные доступны локально; внешние службы не являются единственным мозгом системы"
          ]
        ],
        "y": 56.69,
        "page": 141,
        "old": 41,
        "id": "A41-024",
        "source_ids": [
          "A41-024"
        ],
        "anchor": "A41-024",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Что считать первым законченным экземпляром",
        "y": 338.9935607910156,
        "page": 141,
        "old": 41,
        "id": "A41-025",
        "source_ids": [
          "A41-025"
        ],
        "anchor": "A41-025",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Станция и устройства запускаются по сохранённой конфигурации, приложение подключается без переключения повседневной Wi-Fi-сети, световой сценарий работает без интернета, ручная кнопка сохраняет смысл при отказе сервера, а обновление и восстановление не требуют угадывать скрытые действия автора.",
        "y": 366.0111999511719,
        "page": 141,
        "old": 41,
        "id": "A41-026",
        "source_ids": [
          "A41-026"
        ],
        "anchor": "A41-026",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Следующий экземпляр повторяется по зафиксированным файлам и монтажным картам. При этом реальные номера деталей, размеры, замеры и результаты заменяют проектные предположения. Именно эта замена, а не увеличение числа страниц документа, превращает исследование в инженерно проверенную систему.",
        "y": 438.4151611328125,
        "page": 141,
        "old": 41,
        "id": "A41-027",
        "source_ids": [
          "A41-027"
        ],
        "anchor": "A41-027",
        "content_status": "active"
      },
      {
        "id": "R50-001",
        "kind": "h2",
        "text": "Индекс проверок",
        "source_ids": [
          "R50-001"
        ],
        "anchor": "R50-001",
        "content_status": "active"
      },
      {
        "id": "R50-002",
        "kind": "table",
        "rows": [
          [
            "Карта",
            "Что воспроизводит",
            "Артефакт"
          ],
          [
            "Q-01",
            "Дедуп, версии, поздний ACK, cache eviction",
            "Трассы reference tests; затем MCU/Core"
          ],
          [
            "T-01",
            "Срок в дороге, suspend, reboot, UTC ±1 год",
            "Четыре метки и причины отказа"
          ],
          [
            "S-01",
            "Pin, приглашение, перенос, отзыв, старый backup",
            "Политика ключей; отрицательные handshake на стенде"
          ],
          [
            "MCU-01",
            "TLS 1.3 + BLE + KQIP + rollback",
            "Версии, sdkconfig, partition table, heap/стек"
          ],
          [
            "D-01",
            "ENOSPC, fsync, retention, запись оснований",
            "БД до/после; нет ложного ACCEPTED"
          ],
          [
            "H-01 / ACO-01",
            "Температура, фильтр, шум и микрофоны",
            "Исходные условия, временные ряды, записи"
          ],
          [
            "APP-01",
            "Android/iOS, NSD, FFI, отзыв, доступность",
            "Видео сценария и журнал без секретов"
          ],
          [
            "S/P/A-01",
            "Монтаж датчиков, присутствия и реле",
            "Точная BOM, схема, фото, электрические замеры"
          ]
        ],
        "source_ids": [
          "R50-002"
        ],
        "anchor": "R50-002",
        "content_status": "active"
      },
      {
        "id": "R50-003",
        "kind": "p",
        "text": "Референсный тест Python в комплекте проверяет выбранную семантику модели, а не реальную сеть, TLS или прошивку. PASS переносится на реализацию только после воспроизведения тех же историй на Core/MCU. Неисполненные аппаратные карты остаются OPEN. Так зелёный тест документа не становится ложным разрешением на постоянную эксплуатацию.",
        "source_ids": [
          "R50-003"
        ],
        "anchor": "R50-003",
        "content_status": "active"
      },
      {
        "id": "R50-004",
        "kind": "h2",
        "text": "Дополнения к единому словарю",
        "source_ids": [
          "R50-004"
        ],
        "anchor": "R50-004",
        "content_status": "active"
      },
      {
        "id": "R50-005",
        "kind": "table",
        "rows": [
          [
            "Термин",
            "Смысл"
          ],
          [
            "E0 / E1 / E2",
            "Лабораторный контур / автономная проверенная установка / повторная сборка и перенос"
          ],
          [
            "H0…H5",
            "Существующий ПК, mini-PC, Mini-ITX, Pi, Jetson, Hub + worker"
          ],
          [
            "KQIP",
            "Koobique Interface Protocol; собственный прикладной договор"
          ],
          [
            "BOOT_INHIBIT",
            "Запрет автоматики после запуска MCU до явного освобождения"
          ],
          [
            "ManualOverride",
            "Отдельный ручной режим; E0 без автоматического истечения"
          ],
          [
            "controller_fence",
            "Устойчивое назначение конкретного controller key владельцем"
          ],
          [
            "output_revision / manual_revision",
            "Версии выхода и намерения в конкретном boot_id"
          ],
          [
            "Durable ACK",
            "Подтверждение сохранения, а не только получения байтов"
          ],
          [
            "CBOR / COSE",
            "Двоичное представление / стандартный контейнер подписи"
          ],
          [
            "WAL / OTA / TUF",
            "Журнал БД / обновление устройства / защита метаданных обновления"
          ],
          [
            "Gate / допуск",
            "Проверка условий; её независимость нужно указывать явно"
          ],
          [
            "QVL / VRM / EPS / PPT",
            "Проверенные компоненты / питание CPU / его разъём / лимит мощности"
          ],
          [
            "DTIM",
            "Параметр Wi-Fi, влияющий на режимы экономии и задержки"
          ]
        ],
        "source_ids": [
          "R50-005"
        ],
        "anchor": "R50-005",
        "content_status": "active"
      }
    ],
    "origins": [
      "A41"
    ],
    "question": "Как проверять сборку без пропущенных мелочей?",
    "intro": "Эти карты предназначены для работы у стола. Каждая заканчивается наблюдаемым результатом и действием при отклонении. Проверки стоит отмечать вместе с ревизией платы, прошивкой и условиями — иначе повторный успешный тест трудно сравнить с первым.\n\nСловарь в конце главы возвращает техническим словам простой смысл. Он не заменяет объяснение в тексте, но помогает быстро восстановить контекст, когда вы вернулись к книге через несколько недель.",
    "id": "ch-50",
    "slug": "chapter-50"
  },
  {
    "number": 51,
    "title": "Маршрут самостоятельного изучения",
    "group": "Библиотека",
    "units": [
      {
        "kind": "lead",
        "text": "Учебный материал полезен, когда после него можно объяснить или проверить конкретную часть Koobique.",
        "y": 168.675537109375,
        "page": 119,
        "old": 36,
        "id": "A36-001",
        "source_ids": [
          "A36-001"
        ],
        "anchor": "A36-001",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Ниже — маршрут погружения. Не требуется прочитать все документы до первого измерения, но переход к более рискованному этапу должен сопровождаться пониманием его основы. Ссылки ведут к первичным руководствам и учебным материалам, а не к пересказам без схем.",
        "y": 219.78143310546875,
        "page": 119,
        "old": 36,
        "id": "A36-002",
        "source_ids": [
          "A36-002"
        ],
        "anchor": "A36-002",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Электрические цепи и измерения",
        "y": 299.5755920410156,
        "page": 119,
        "old": 36,
        "id": "A36-003",
        "source_ids": [
          "A36-003"
        ],
        "anchor": "A36-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "MIT 6.002, Circuits and Electronics помогает разобраться в законах цепей, транзисторах, динамике и измерении. Практическое упражнение: рассчитать ток лампы, потери в проводе и шунте, затем измерить их. Объяснить, почему порог MOSFET не равен гарантированному открыванию. [68][80]",
        "y": 326.5932312011719,
        "page": 119,
        "old": 36,
        "id": "A36-004",
        "source_ids": [
          "A36-004"
        ],
        "anchor": "A36-004",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "NXP UM10204 нужен для понимания открытого стока, адресации и временны́ х параметров I²C. Упражнение: найти подтяжки на двух breakout, рассчитать эквивалентное сопротивление и сравнить фронт с осциллограммой. [26]",
        "y": 398.9971618652344,
        "page": 119,
        "old": 36,
        "id": "A36-005",
        "source_ids": [
          "A36-005"
        ],
        "anchor": "A36-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Datasheets SHT45, VEML7700, INA226 учат отличать диапазон, точность, разрешение и условия измерения. Упражнение: составить одностраничную карточку каждого сенсора с питанием, адресом, задержкой и флагами ошибки. [15][17][79]",
        "y": 455.42608642578125,
        "page": 119,
        "old": 36,
        "id": "A36-006",
        "source_ids": [
          "A36-006"
        ],
        "anchor": "A36-006",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Конструкция и материалы",
        "y": 519.2453002929688,
        "page": 119,
        "old": 36,
        "id": "A36-007",
        "source_ids": [
          "A36-007"
        ],
        "anchor": "A36-007",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "FreeCAD — основа параметрической модели. Упражнение: сделать раму с параметром внешней грани, затем изменить 320 на 340 мм и проверить, какие размеры должны измениться автоматически, а какие остаются привязаны к реальной плате. [13]",
        "y": 546.262939453125,
        "page": 119,
        "old": 36,
        "id": "A36-008",
        "source_ids": [
          "A36-008"
        ],
        "anchor": "A36-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Документация вентиляторов и Prusa помогает отделить геометрию, электрический интерфейс и свойства печати. Упражнение: изготовить купон вставок и кассету фильтра, измерить посадку и сравнить открытый/закрытый корпус. [4][6][11][12]",
        "y": 602.69189453125,
        "page": 119,
        "old": 36,
        "id": "A36-009",
        "source_ids": [
          "A36-009"
        ],
        "anchor": "A36-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Механические файлы производителей нужны для посадок. Упражнение: импортировать реальный габарит платы, добавить объёмы штекеров и траекторию извлечения фильтра. Красивый вид модели не считается проверкой обслуживаемости. [3][88]",
        "y": 659.1207885742188,
        "page": 119,
        "old": 36,
        "id": "A36-010",
        "source_ids": [
          "A36-010"
        ],
        "anchor": "A36-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "PCB и embedded Rust",
        "y": 56.314456939697266,
        "page": 120,
        "old": 36,
        "id": "A36-011",
        "source_ids": [
          "A36-011"
        ],
        "anchor": "A36-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "KiCad Getting Started показывает полный путь от схемы к производственным файлам. Упражнение: маленький носитель датчика с разъёмом, подтяжками, тестовыми точками и собственным footprint, сверенным по чертежу. Перед заказом распечатать 1:1. [27]",
        "y": 83.33208465576172,
        "page": 120,
        "old": 36,
        "id": "A36-012",
        "source_ids": [
          "A36-012"
        ],
        "anchor": "A36-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "The Embedded Rust Book объясняет периферию, владение, прерывания и no_std. The Rust on ESP Book связывает эти идеи с конкретными ESP-инструментами. Упражнение: драйвер сенсора с тайм-аутом и ошибками, затем имитация отсутствующего устройства. [44][31]",
        "y": 139.76101684570312,
        "page": 120,
        "old": 36,
        "id": "A36-013",
        "source_ids": [
          "A36-013"
        ],
        "anchor": "A36-013",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "ESP-IDF OTA / Secure Boot / Provisioning изучаются до записи необратимых защитных настроек. Упражнение: прервать обновление тестовой платы и восстановиться по документированной процедуре. [35][36][70]",
        "y": 212.16497802734375,
        "page": 120,
        "old": 36,
        "id": "A36-014",
        "source_ids": [
          "A36-014"
        ],
        "anchor": "A36-014",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Rust-сервер и протокол",
        "y": 275.984130859375,
        "page": 120,
        "old": 36,
        "id": "A36-015",
        "source_ids": [
          "A36-015"
        ],
        "anchor": "A36-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "The Rust Programming Language и Tokio Tutorial дают основу владения и асинхронности. Упражнение: ограниченная очередь событий с измерением возраста и политики переполнения. Объяснить, почему async не делает блокирующую операцию неблокирующей. [43][45][82]",
        "y": 303.00177001953125,
        "page": 120,
        "old": 36,
        "id": "A36-016",
        "source_ids": [
          "A36-016"
        ],
        "anchor": "A36-016",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "RFC 8949 и документация Rustls нужны для wire-профиля. Упражнение: закодировать один Observation, сравнить байты на хосте и MCU, затем отклонить пакет с повторяющимся ключом и чрезмерной длиной. [48][47]",
        "y": 375.40570068359375,
        "page": 120,
        "old": 36,
        "id": "A36-017",
        "source_ids": [
          "A36-017"
        ],
        "anchor": "A36-017",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Rust Fuzz Book и Loom помогают проверять парсеры и небольшие конкурентные участки. Упражнение: сформулировать свойство, которое должно сохраняться при любом порядке событий, прежде чем писать тест. [95][86]",
        "y": 431.83465576171875,
        "page": 120,
        "old": 36,
        "id": "A36-018",
        "source_ids": [
          "A36-018"
        ],
        "anchor": "A36-018",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Физические модели и данные",
        "y": 495.6538391113281,
        "page": 120,
        "old": 36,
        "id": "A36-019",
        "source_ids": [
          "A36-019"
        ],
        "anchor": "A36-019",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "MIT 6.302, Feedback Systems объясняет обратную связь, устойчивость и динамику. Упражнение: смоделировать комнату первого порядка, добавить задержку датчика и увидеть влияние шага и усиления. Начинать на симуляторе, не на реальном нагревателе. [69]",
        "y": 522.6714477539062,
        "page": 120,
        "old": 36,
        "id": "A36-020",
        "source_ids": [
          "A36-020"
        ],
        "anchor": "A36-020",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "SQLite WAL / Backup нужны для сохранности, а не только запросов. Упражнение: команда с outbox в одной транзакции, аварийная остановка и продолжение с тем же ID. [51][52][93]",
        "y": 595.0753784179688,
        "page": 120,
        "old": 36,
        "id": "A36-021",
        "source_ids": [
          "A36-021"
        ],
        "anchor": "A36-021",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "KMP и голос",
        "y": 658.8945922851562,
        "page": 120,
        "old": 36,
        "id": "A36-022",
        "source_ids": [
          "A36-022"
        ],
        "anchor": "A36-022",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Kotlin Multiplatform, UniFFI и платформенные разрешения помогают построить границы приложения. Упражнение: подключиться к локальному тестовому API на Android и iOS, отозвать разрешение и показать пользователю понятный путь восстановления. [54][55][56][57]",
        "y": 685.9122314453125,
        "page": 120,
        "old": 36,
        "id": "A36-023",
        "source_ids": [
          "A36-023"
        ],
        "anchor": "A36-023",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "whisper.cpp, sherpa-onnx и Vosk изучаются сравнением одного аудиокорпуса. Упражнение: измерить не только текстовую ошибку, но и ошибку объекта, ложное выполнение и задержку после конца фразы. [61][62][99]",
        "y": 57.42815399169922,
        "page": 121,
        "old": 36,
        "id": "A36-024",
        "source_ids": [
          "A36-024"
        ],
        "anchor": "A36-024",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Silero, openWakeWord и документация массива изучаются после эталонного микрофона. Упражнение: шум вентилятора, собственный ответ динамика и обычная речь без команды не должны вызывать действие. [63][64][67]",
        "y": 113.85709381103516,
        "page": 121,
        "old": 36,
        "id": "A36-025",
        "source_ids": [
          "A36-025"
        ],
        "anchor": "A36-025",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Как вести инженерный дневник",
        "y": 177.67625427246094,
        "page": 121,
        "old": 36,
        "id": "A36-026",
        "source_ids": [
          "A36-026"
        ],
        "anchor": "A36-026",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Каждый опыт занимает небольшую карточку: цель, схема, версии, условия, ожидаемый результат, измерение, вывод и следующий шаг. Сохраняются исходные файлы, а не только скриншот графика. Отрицательный результат тоже полезен, если понятно, что именно проверялось.",
        "y": 204.69390869140625,
        "page": 121,
        "old": 36,
        "id": "A36-027",
        "source_ids": [
          "A36-027"
        ],
        "anchor": "A36-027",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "После этого маршрута подготовка первого экземпляра становится последовательностью проверяемых решений. Сначала доказывается цепочка кабинета, затем выбирается автономное железо, затем изготавливается корпус, и только потом уменьшаются детали, потребление и стоимость.",
        "y": 277.09783935546875,
        "page": 121,
        "old": 36,
        "id": "A36-028",
        "source_ids": [
          "A36-028"
        ],
        "anchor": "A36-028",
        "content_status": "active"
      },
      {
        "kind": "lead",
        "text": "Каждый блок материалов заканчивается небольшим результатом, который можно получить на своём ПК или безопасном стенде.",
        "id": "I26-001",
        "old": "I26",
        "source_ids": [
          "I26-001"
        ],
        "anchor": "I26-001",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Маршрут A. Измерить и не обмануть себя",
        "id": "I26-002",
        "old": "I26",
        "source_ids": [
          "I26-002"
        ],
        "anchor": "I26-002",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Изучить: вводный GUM и VIM, SOSA/SSN, Industrial I/O, документацию ADC выбранного микроконтроллера. [100] [101] [105] [141]",
        "id": "I26-003",
        "old": "I26",
        "source_ids": [
          "I26-003"
        ],
        "anchor": "I26-003",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сделать: паспорт одного канала; записать 15 минут наблюдений с пропуском питания; построить бюджет неопределённости без смешения стандартной ошибки и предельного допуска. Отдельно объяснить, что случится после перестановки сенсора.",
        "id": "I26-004",
        "old": "I26",
        "source_ids": [
          "I26-004"
        ],
        "anchor": "I26-004",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Маршрут B. Построить малую модель",
        "id": "I26-005",
        "old": "I26",
        "source_ids": [
          "I26-005"
        ],
        "anchor": "I26-005",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Изучить: MIT System Identification и Robust/Stochastic Control; затем do-mpc и OSQP только в объёме своей задачи. [111] [110] [112] [113]",
        "id": "I26-006",
        "old": "I26",
        "source_ids": [
          "I26-006"
        ],
        "anchor": "I26-006",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сделать: определить вклад лампы, сравнить одноступенчатый и многократный прогноз, найти режим, где модель перестаёт подходить. В симуляции проверить бинарный план и невозможную цель. Не применять случайные исследовательские действия к опасным нагрузкам.",
        "id": "I26-007",
        "old": "I26",
        "source_ids": [
          "I26-007"
        ],
        "anchor": "I26-007",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Маршрут C. Понять AI без смешения задач",
        "id": "I26-008",
        "old": "I26",
        "source_ids": [
          "I26-008"
        ],
        "anchor": "I26-008",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Изучить: формы решений Jev и опубликованные ограничения; статьи V-JEPA 2/2.1; для дальнейшего исследования — DreamerV3, TD-MPC2, PETS. [115] [118] [121] [122] [124] [125] [126]",
        "id": "I26-009",
        "old": "I26",
        "source_ids": [
          "I26-009"
        ],
        "anchor": "I26-009",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сделать: для каждой технологии написать один абзац: вход, выход, данные обучения, что она не доказывает. Спроектировать локальный интерфейс выбора намерения с вариантами «уточнить» и «не поддерживается». Не оценивать модель только на красивых положительных примерах.",
        "id": "I26-010",
        "old": "I26",
        "source_ids": [
          "I26-010"
        ],
        "anchor": "I26-010",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Маршрут D. Проверить вероятность и перенос",
        "id": "I26-011",
        "old": "I26",
        "source_ids": [
          "I26-011"
        ],
        "anchor": "I26-011",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Изучить: Guo и соавторы о калибровке, адаптивном конформном прогнозировании, работы об информационно-ориентированном выборе действий. [128] [129] [130] [131]",
        "id": "I26-012",
        "old": "I26",
        "source_ids": [
          "I26-012"
        ],
        "anchor": "I26-012",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сделать: разделить эпизоды по дням; построить таблицу ошибок по сценариям и сравнить две модели при одной цене ошибок. Показать, почему хороший средний процент скрывает плохой режим неподвижного присутствия.",
        "id": "I26-013",
        "old": "I26",
        "source_ids": [
          "I26-013"
        ],
        "anchor": "I26-013",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Маршрут E. Встроить модель в систему",
        "id": "I26-014",
        "old": "I26",
        "source_ids": [
          "I26-014"
        ],
        "anchor": "I26-014",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Изучить: Tokio и блокирующие операции, cgroups v2, выбранный Rust ML-runtime, TUF и формат тензорных артефактов. [82] [132] [73] [134] [135] [136] [84] [138]",
        "id": "I26-015",
        "old": "I26",
        "source_ids": [
          "I26-015"
        ],
        "anchor": "I26-015",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Сделать: описать контракт worker и полный манифест модели; задать лимит памяти; воспроизвести отказ worker, не меняя состояния лампы. Фактическая разработка следует отдельному этапу после согласования контрактов.",
        "id": "I26-016",
        "old": "I26",
        "source_ids": [
          "I26-016"
        ],
        "anchor": "I26-016",
        "content_status": "active"
      },
      {
        "kind": "h2",
        "text": "Короткий словарь",
        "id": "I26-017",
        "old": "I26",
        "source_ids": [
          "I26-017"
        ],
        "anchor": "I26-017",
        "content_status": "active"
      },
      {
        "kind": "table",
        "rows": [
          [
            "Термин",
            "Практический смысл"
          ],
          [
            "Наблюдаемость",
            "Можно ли различить нужные состояния по доступным данным"
          ],
          [
            "Идентификация",
            "Определение модели или её параметров из измерений и действий"
          ],
          [
            "Латентное состояние",
            "Внутреннее представление модели, не прямое измерение"
          ],
          [
            "Калибровка вероятностей",
            "Соответствие численных прогнозов частотам исходов в проверенной постановке"
          ],
          [
            "Область применимости",
            "Условия, в которых модель проверена и допущена"
          ],
          [
            "MPC",
            "Повторное планирование на ограниченном горизонте по модели"
          ],
          [
            "Инвариант",
            "Условие, которое исполнитель обязан сохранять"
          ],
          [
            "Идемпотентность",
            "Повтор операции не меняет её логический итог после первого успеха"
          ],
          [
            "Теневой режим",
            "Модель предлагает, но не получает право физического действия"
          ],
          [
            "Происхождение данных",
            "Связь записи с источниками, преобразованиями и версиями"
          ],
          [
            "Версии / boot / fence",
            "Отдельные шкалы; назначение контроллера не смешивается с ручным режимом"
          ],
          [
            "Контрфактический исход",
            "Что произошло бы при другом действии; не наблюдается прямо в старом журнале"
          ]
        ],
        "id": "I26-018",
        "old": "I26",
        "source_ids": [
          "I26-018"
        ],
        "anchor": "I26-018",
        "content_status": "active"
      },
      {
        "kind": "p",
        "text": "Полные названия материалов и ссылки приведены в едином списке источников. Этот маршрут дополняет практические задания по электронике, механике, Linux, KMP и протоколу: сначала воспроизводится небольшой опыт, затем читается документация к тому механизму, который в нём встретился.",
        "id": "I26-019",
        "old": "I26",
        "edited": true,
        "source_ids": [
          "I26-019"
        ],
        "anchor": "I26-019",
        "content_status": "active"
      },
      {
        "id": "R51-001",
        "kind": "h2",
        "text": "Учебный маршрут для закрытия рисков, а не только чтения",
        "source_ids": [
          "R51-001"
        ],
        "anchor": "R51-001",
        "content_status": "active"
      },
      {
        "id": "R51-002",
        "kind": "table",
        "rows": [
          [
            "Тема",
            "Первичный материал",
            "Практика перед следующим этапом"
          ],
          [
            "Время и распределённые события",
            "Linux clock_gettime, ESP System Time, RFC 5905",
            "Смоделировать асимметрию, поздний пакет и reboot"
          ],
          [
            "PKI и локальный pin",
            "RFC 5280/7250; Rustls verifier",
            "Чужой ключ всегда отвергается; дата не даёт доверия"
          ],
          [
            "MCU и защита",
            "ESP-IDF NVS, security workflow, OTA",
            "Запасная плата; версии и recovery до eFuse"
          ],
          [
            "Linux-службы",
            "systemd network и service",
            "Старт без роутера; зависание критической задачи"
          ],
          [
            "KMP и сеть",
            "UniFFI, Gobley, Android NSD, Apple TN3179",
            "Реальные Android/iOS; отказ разрешения; cancellation"
          ],
          [
            "Происхождение ПО",
            "RustSec, cargo-deny, манифест",
            "Чистая машина воспроизводит артефакты и хеши"
          ]
        ],
        "source_ids": [
          "R51-002"
        ],
        "anchor": "R51-002",
        "content_status": "active"
      },
      {
        "id": "R51-003",
        "kind": "p",
        "text": "Результат чтения — небольшой опыт с ожидаемым исходом. До пайки нужно уметь проверить полярность, отсутствие короткого замыкания, токовое ограничение и безопасное снятие питания; при отсутствии этого опыта электрическая часть выполняется с квалифицированной помощью. Ни ссылка, ни книга не заменяют методику работы с конкретным прибором.",
        "source_ids": [
          "R51-003"
        ],
        "anchor": "R51-003",
        "content_status": "active"
      },
      {
        "id": "R51-004",
        "kind": "p",
        "text": "Первичные документы, симуляции и референсные тесты используются для разных проверок. Сохранённый журнал решений связывает изменения с их основаниями; исходные материалы остаются доступными для сравнения редакций. Результат программной проверки не является аппаратным испытанием или внешним аудитом. История подготовки редакций и границы проведённого ревью вынесены на страницу доказательств.",
        "source_ids": [
          "R51-004"
        ],
        "anchor": "R51-004",
        "content_status": "active"
      }
    ],
    "origins": [
      "A36",
      "I26"
    ],
    "question": "Что читать дальше и какой опыт сделать после чтения?",
    "intro": "Не нужно сначала изучать всю электронику, теорию управления и машинное обучение целиком. Двигайтесь от вопроса на стенде к соответствующему источнику: фронт I²C — к шине и подтяжкам, поздняя запись — к времени и буферам, колебания лампы — к обратной связи.\n\nДля каждой темы полезно сделать маленький опыт, записать результат и только затем усложнить систему. Так документация превращается из длинного списка ссылок в инструмент, которым можно пользоваться при реальной сборке.",
    "id": "ch-51",
    "slug": "chapter-51"
  }
]
