firmware-stability — от «код работает» до «продукт готов»
Подскилл code-audit-core. Аудит отвечает на вопрос «что не так в тексте программы»;
этот скилл — на вопрос «что не так в работающей системе и чего ей не хватает до выпуска».
Написан для любой встроенной платформы; платформенное вынесено в references/.
Главный закон: сначала наблюдаемость, потом нагрузка
Прогон, который не оставляет следов, — потраченное время, а не эксперимент. Типовая
история: устройство встало на три часа, само ожило, в логах ничего. Событие произошло —
разбирать нечего, и всё повторится. Поэтому порядок жёсткий: сначала сделать отказ
наблюдаемым, и только потом нагружать. До запуска ответить письменно на три вопроса:
- Если задача зависнет — что останется в доказательство? (сторожевой таймер: он
перезагружает или только пишет предупреждение? — см.
references/esp-idf-diagnostics.md)
- Если система перезагрузится — сохранится ли посмертный дамп, и есть ли куда его класть?
- Если деградация будет плавной — какие счётчики её покажут, и пишутся ли они уже сейчас?
Нет ответа хотя бы на один — прогон откладывается, чинится наблюдаемость. Это не
бюрократия: без неё редкий отказ придётся ловить заново, а он редкий.
Разметка достоверности — обязательна во всех документах скилла
| Метка |
Значение |
| [Ф] |
факт, проверен в исходнике или выводом команды — с файл:строка либо командой |
| [В] |
внешний факт с источником и уровнем ✅/📗 по методике sci-search |
| [Г] |
гипотеза, выведенная из [Ф]+[В], но не подтверждённая замером |
| [П] |
предлагаемая проверка, ещё не выполненная |
Смешивать запрещено. «Наверное, из-за флеша» — это [Г], и обязано быть помечено как [Г],
пока нет замера. Непомеченная гипотеза в отчёте читается как факт и отравляет решения.
Лестница фаз
Порядок обусловлен ценой ошибки и ценой самого шага: дешёвое и воспроизводимое — раньше.
| Фаза |
Что |
Почему здесь |
| 0. Инвентаризация |
что за система: задачи, приоритеты, ядра, память, хранилище, интерфейсы, чего в ней НЕТ |
без карты дальше гадание; «чего нет» отсекает целые ветки поиска |
| 1. Статика |
аудит кода по линзам code-audit-core, упор на параллелизм и ресурсы |
найденный за столом дефект дешевле пойманного сутками прогона |
| 2. Хост-тесты |
модели узлов на обычном ПК, фаззинг парсеров |
воспроизводимость за секунды вместо часов на железе |
| 3. Наблюдаемость |
сторожевые таймеры, посмертные дампы, счётчики, отдельная диагностическая сборка |
главный закон выше |
| 4. Базовые метрики |
стеки, куча, загрузка ядер, таймеры — в покое и под типовой нагрузкой |
нужна точка отсчёта, иначе «стало хуже» не с чем сравнить |
| 5. Нагрузка и время |
длительный прогон, все подсистемы одновременно |
отказы этого класса появляются только на масштабе времени |
| 6. Хаос |
отключения, разрывы, снятие питания, мусор на входе |
проверяется восстановление, а не работа |
| 7. Целостность |
сквозная сверка данных независимыми путями |
последний рубеж: система жива, но данные врут |
| 8. Приёмка |
критерии готовности, references/release-checklist.md |
решение о выпуске — по числам, не по ощущению |
Пропускать фазы можно, но осознанно и с записью причины. Чаще всего пропуск фазы 3
оборачивается возвратом к ней после первого же необъяснённого прогона.
Классы отказов и где их искать
| Класс |
Как выглядит снаружи |
Где искать в первую очередь |
| Зависание задачи |
«замерло», часть функций жива |
сторожевой таймер, приоритеты, блокирующие вызовы под локом |
| Фриз всей системы |
замирает всё, включая связь |
долгие операции с запретом прерываний или отключением кэша |
| Плавная деградация |
«через сутки тормозит» |
фрагментация кучи, рост структур, износ и заполнение хранилища |
| Утечка ресурса |
падает через N часов |
дескрипторы, буферы, сокеты, задачи, не освобождённые на путях ошибок |
| Гонка |
«иногда», не воспроизводится |
общее состояние между обработчиком и фоновой задачей |
| Потеря данных |
пробелы в записи |
буфер перезаписан раньше, чем потребитель забрал; проверить, кто кого догоняет |
| Тихий отказ |
функция молча не работает |
проглоченные коды возврата, лог об успехе до проверки результата |
| Отказ под нагрузкой |
ошибки только при совмещении |
конкуренция за единственный ресурс, таймауты меньше физического времени операции |
Приём: бюджет времени против таймаута
Класс дефектов, который статический аудит не ловит, а замер ловит сразу: в коде стоит
таймаут, физически меньший, чем время операции, которую он ждёт.
Метод, три шага:
- [Ф] Выписать из кода все таймауты и окна ожидания — с
файл:строка.
- [В] Найти в документации и паспортах компонентов физическое время операций, которых
эти таймауты ждут: стирание и запись памяти, обмен по шине, установление соединения.
- Сопоставить арифметикой, а не на глаз: объём разделить на гранулярность, умножить на
время операции, взять типовое и предельное значения. Расхождение на порядок — находка.
Считать скриптом и печатать промежуточные величины. Ошибка в устном счёте здесь стоит
неверного вывода: проверено на себе — условие в однострочнике дало одинаковый множитель
для двух разных гранулярностей, числа разошлись втрое и были пойманы только пересчётом.
Типовые источники «физики» — references/flash-storage.md.
Приём: что система делает МОЛЧА
Отдельно проверяется не поведение, а его отсутствие:
- Возвращаемые значения, которые никто не смотрит: удаление, запись, освобождение.
- Лог об успехе, стоящий до проверки результата или без неё вовсе.
- Флаг «сделано», сбрасываемый раньше, чем действие подтверждено, — повтора не будет
никогда, временный сбой превращается в постоянное состояние.
- Ветки ошибок без освобождения захваченного ресурса — каждая утечка одноразова, но
повторяется при каждой ошибке.
- Счётчики потерь, которые считаются, но никуда не выводятся.
Это самый урожайный проход по чужой прошивке и самый неприятный по последствиям:
такие дефекты не падают, а тихо портят данные.
Кто исполняет проверки и откуда берётся стерильность
Работа идёт на самой нижней ступени, которая её тянет: обход исходников, счёт и арифметика
бюджета — скрипт; разбор логов прогона, классификация и суммаризация — локальная
модель (суточный лог в контекст не заходит вовсе); суждение «дефект или намеренно» —
субагент; вердикт и решение о выпуске — не делегируется.
Стерильность — свойство контекста, а не модели. Проверяющий проход не должен видеть ни
работы, ни выводов проверяемого: названный ответ подтверждают, а не проверяют. В бриф идут
задача, метод и критерий приёмки — и никогда ожидаемые числа. Числа проверяются пересчётом
от первичных величин, а не сверкой с уже напечатанным: сверка ловит опечатку, но
подтверждает ошибку в самом расчёте. Состояние — чтением результата командой, а не памятью о
том, что команда вызывалась.
Необратимое и критичное — два прохода, не видящих друг друга. При расхождении побеждает
предъявивший файл:строка, вывод команды или число замера, а не более уверенный.
Дробить проверку на короткие шаги. Единственный длинный фоновый проход не переживает
перезапуск инструмента и теряет результат целиком; скриптовые проверки живучее прочих и идут
первыми. Тело правила, таблицы ступеней и разбор — references/execution-and-sterility.md.
Границы и дисциплина
Необратимое — только оператор. Снятие питания на живой записи, стирание разделов,
перепрошивка без пути отката, любые деструктивные команды прибору — предлагаются, но не
выполняются. Отдельно: если пути отката нет физически (нет резервной копии прошивки,
корпус неразборный) — это стоп, а не «аккуратно попробуем».
Диагностическая сборка ≠ релизная. Всё, что включено ради наблюдаемости, помечается и
в выпуск не уезжает. Обратное тоже верно: измерять надо ту сборку, которую выпускают, иначе
меряется не тот объект.
Чужая зона (§ 12). Прошивку чужого контура не правим: находки, доказательства
файл:строка, промпт владельцу. Рецензия чужого аудита — профильная работа
(actor ≠ verifier), делается по исходному коду, а не по пересказу.
Замер «до» обязателен. Сначала телеметрия как есть, потом изменения. Иначе «починили»
неотличимо от «не воспроизвелось» — а это разные вещи с разной ценой.
Справочники
| Файл |
Содержимое |
references/esp-idf-diagnostics.md |
штатные средства ESP-IDF и FreeRTOS: сторожевые таймеры, дампы, трассировка, метрики кучи и стеков — с источниками |
references/flash-storage.md |
тайминги NOR-флеш, поведение файловых систем при обрыве питания и при заполнении, следствия для таймаутов |
references/test-methods.md |
методология: длительные испытания, фаззинг, внесение отказов, испытания с оборудованием в контуре — с источниками |
references/release-checklist.md |
критерии готовности продукта, чек-лист приёмки, форма отчёта |
references/execution-and-sterility.md |
кто исполняет проверки, лестница ступеней, стерильность проходов, живучесть |
references/testplan-atomspectra-waterfall.md |
ПРИМЕР полного прогона лестницы 0-8 на реальном проекте (прошивка ESP32 + ПК-приёмник, 2026-08-20) — шаблон структуры плана, переносимые уроки процесса |
scripts/aswf/verify_aswf.py |
независимая проверка целостности .aswf-файлов (CRC32 построчно) — извлечён из прогона выше, переиспользуемый инструмент |
scripts/doc-inventory/docs_inventory.py |
Ollama-инвентаризация md-документации (функции/интерфейсы/форматы/ограничения из README и т.п.) — не проектно-специфичен, годен для любой прошивки с доками |
Тест-план: диагностика незнакомой прошивки
- Инвентаризация (фаза 0) — карта задач, память, хранилище; отдельно записать, чего в
системе нет: это сокращает работу сильнее, чем любая находка.
- Прочитать её собственные разборы прошлых отказов, если есть, — чтобы не переоткрывать
уже починенное, а проверять полноту фиксов.
- Статика по линзам
code-audit-core, приоритет — «размер × риск».
- Приём «бюджет против таймаута» и приём «что делает молча» — оба дают находки до железа.
- Наблюдаемость (фаза 3) — до первого длительного прогона, без исключений.
- Базовые метрики, затем нагрузка, хаос, целостность.
- Приёмка по
references/release-checklist.md; невыполненные пункты — в отчёт как есть,
без округления в свою пользу.
Анти-паттерны
| Избегать |
Почему |
| Суточный прогон без наблюдаемости |
отказ произойдёт и не оставит следов; время потрачено |
| «Наверное, из-за X» без метки [Г] |
гипотеза читается как факт и определяет решения |
| Устный счёт бюджета времени |
множители путаются; считать скриптом с промежуточными числами |
| Диагностическая сборка в релиз |
меняет тайминги; выпускается не то, что измеряли |
| Мерить одну подсистему в изоляции |
отказы этого класса живут на стыках, а не внутри узлов |
| Чинить найденное до конца измерений |
пропадает база сравнения; сначала зафиксировать «до» |
| «Не воспроизводится» как вывод |
это не вывод, а состояние: значит, не хватает наблюдаемости |
| Считать зелёный прогон доказательством |
отсутствие отказа за сутки не равно его отсутствию; писать длительность и условия |
Связанные скиллы
code-audit-core — мастер-скилл: линзы, шкала P0–P3, форма отчёта.
sci-search — внешние факты и уровни ✅/📗 для меток [В].
~/.claude/references/systematic-debugging.md (#DBG-1) — один воспроизводимый баг, а не поиск
неизвестных: сначала причина, потом правка; три неудачных фикса — вопрос к архитектуре.
product-dev-cycle — тест до кода, мутационная приёмка, цикл ветки, приём критики.
six-corner-audit — гейт перед мержем и публикацией.
incident-log — фиксация случившегося отказа и уроков из него.
md-doc — вёрстка отчёта и плана для оператора.
1---2name: firmware-stability3description: Прошивка → продукт: зависания, фризы, лаги, потери данных, утечки, гонки, деградация за сутки; наблюдаемость, soak/endurance, фаззинг, приёмка. «Плату наизнанку», «не воспроизводится», «готова ли к релизу». Чтение кода на дефекты → `code-audit-core`.4---56# firmware-stability — от «код работает» до «продукт готов»78> Подскилл `code-audit-core`. Аудит отвечает на вопрос «что не так в тексте программы»;9> этот скилл — на вопрос «что не так в работающей системе и чего ей не хватает до выпуска».10> Написан для любой встроенной платформы; платформенное вынесено в `references/`.1112## Главный закон: сначала наблюдаемость, потом нагрузка1314**Прогон, который не оставляет следов, — потраченное время, а не эксперимент.** Типовая15история: устройство встало на три часа, само ожило, в логах ничего. Событие произошло —16разбирать нечего, и всё повторится. Поэтому порядок жёсткий: **сначала сделать отказ17наблюдаемым, и только потом нагружать.** До запуска ответить письменно на три вопроса:18191. Если задача зависнет — что останется в доказательство? (сторожевой таймер: он20 перезагружает или только пишет предупреждение? — см. `references/esp-idf-diagnostics.md`)212. Если система перезагрузится — сохранится ли посмертный дамп, и есть ли куда его класть?223. Если деградация будет плавной — какие счётчики её покажут, и пишутся ли они уже сейчас?2324Нет ответа хотя бы на один — прогон откладывается, чинится наблюдаемость. Это не25бюрократия: без неё редкий отказ придётся ловить заново, а он редкий.2627## Разметка достоверности — обязательна во всех документах скилла2829| Метка | Значение |30|---|---|31| **[Ф]** | факт, проверен в исходнике или выводом команды — с `файл:строка` либо командой |32| **[В]** | внешний факт с источником и уровнем ✅/📗 по методике `sci-search` |33| **[Г]** | гипотеза, выведенная из [Ф]+[В], но не подтверждённая замером |34| **[П]** | предлагаемая проверка, ещё не выполненная |3536Смешивать запрещено. «Наверное, из-за флеша» — это [Г], и обязано быть помечено как [Г],37пока нет замера. Непомеченная гипотеза в отчёте читается как факт и отравляет решения.3839## Лестница фаз4041Порядок обусловлен ценой ошибки и ценой самого шага: дешёвое и воспроизводимое — раньше.4243| Фаза | Что | Почему здесь |44|---|---|---|45| **0. Инвентаризация** | что за система: задачи, приоритеты, ядра, память, хранилище, интерфейсы, чего в ней НЕТ | без карты дальше гадание; «чего нет» отсекает целые ветки поиска |46| **1. Статика** | аудит кода по линзам `code-audit-core`, упор на параллелизм и ресурсы | найденный за столом дефект дешевле пойманного сутками прогона |47| **2. Хост-тесты** | модели узлов на обычном ПК, фаззинг парсеров | воспроизводимость за секунды вместо часов на железе |48| **3. Наблюдаемость** | сторожевые таймеры, посмертные дампы, счётчики, отдельная диагностическая сборка | главный закон выше |49| **4. Базовые метрики** | стеки, куча, загрузка ядер, таймеры — в покое и под типовой нагрузкой | нужна точка отсчёта, иначе «стало хуже» не с чем сравнить |50| **5. Нагрузка и время** | длительный прогон, все подсистемы одновременно | отказы этого класса появляются только на масштабе времени |51| **6. Хаос** | отключения, разрывы, снятие питания, мусор на входе | проверяется восстановление, а не работа |52| **7. Целостность** | сквозная сверка данных независимыми путями | последний рубеж: система жива, но данные врут |53| **8. Приёмка** | критерии готовности, `references/release-checklist.md` | решение о выпуске — по числам, не по ощущению |5455Пропускать фазы можно, но осознанно и с записью причины. Чаще всего пропуск фазы 356оборачивается возвратом к ней после первого же необъяснённого прогона.5758## Классы отказов и где их искать5960| Класс | Как выглядит снаружи | Где искать в первую очередь |61|---|---|---|62| Зависание задачи | «замерло», часть функций жива | сторожевой таймер, приоритеты, блокирующие вызовы под локом |63| Фриз всей системы | замирает всё, включая связь | долгие операции с запретом прерываний или отключением кэша |64| Плавная деградация | «через сутки тормозит» | фрагментация кучи, рост структур, износ и заполнение хранилища |65| Утечка ресурса | падает через N часов | дескрипторы, буферы, сокеты, задачи, не освобождённые на путях ошибок |66| Гонка | «иногда», не воспроизводится | общее состояние между обработчиком и фоновой задачей |67| Потеря данных | пробелы в записи | буфер перезаписан раньше, чем потребитель забрал; проверить, кто кого догоняет |68| Тихий отказ | функция молча не работает | проглоченные коды возврата, лог об успехе до проверки результата |69| Отказ под нагрузкой | ошибки только при совмещении | конкуренция за единственный ресурс, таймауты меньше физического времени операции |7071## Приём: бюджет времени против таймаута7273Класс дефектов, который статический аудит не ловит, а замер ловит сразу: **в коде стоит74таймаут, физически меньший, чем время операции, которую он ждёт.**7576Метод, три шага:77781. **[Ф]** Выписать из кода все таймауты и окна ожидания — с `файл:строка`.792. **[В]** Найти в документации и паспортах компонентов физическое время операций, которых80 эти таймауты ждут: стирание и запись памяти, обмен по шине, установление соединения.813. Сопоставить **арифметикой, а не на глаз**: объём разделить на гранулярность, умножить на82 время операции, взять типовое и предельное значения. Расхождение на порядок — находка.8384Считать скриптом и печатать промежуточные величины. Ошибка в устном счёте здесь стоит85неверного вывода: проверено на себе — условие в однострочнике дало одинаковый множитель86для двух разных гранулярностей, числа разошлись втрое и были пойманы только пересчётом.8788Типовые источники «физики» — `references/flash-storage.md`.8990## Приём: что система делает МОЛЧА9192Отдельно проверяется не поведение, а его отсутствие:9394- Возвращаемые значения, которые никто не смотрит: удаление, запись, освобождение.95- Лог об успехе, стоящий **до** проверки результата или без неё вовсе.96- Флаг «сделано», сбрасываемый раньше, чем действие подтверждено, — повтора не будет97 никогда, временный сбой превращается в постоянное состояние.98- Ветки ошибок без освобождения захваченного ресурса — каждая утечка одноразова, но99 повторяется при каждой ошибке.100- Счётчики потерь, которые считаются, но никуда не выводятся.101102Это самый урожайный проход по чужой прошивке и самый неприятный по последствиям:103такие дефекты не падают, а тихо портят данные.104105## Кто исполняет проверки и откуда берётся стерильность106107Работа идёт на самой нижней ступени, которая её тянет: обход исходников, счёт и арифметика108бюджета — **скрипт**; разбор логов прогона, классификация и суммаризация — **локальная109модель** (суточный лог в контекст не заходит вовсе); суждение «дефект или намеренно» —110**субагент**; вердикт и решение о выпуске — **не делегируется**.111112**Стерильность — свойство контекста, а не модели.** Проверяющий проход не должен видеть ни113работы, ни выводов проверяемого: названный ответ подтверждают, а не проверяют. В бриф идут114задача, метод и критерий приёмки — и никогда ожидаемые числа. Числа проверяются пересчётом115**от первичных величин**, а не сверкой с уже напечатанным: сверка ловит опечатку, но116подтверждает ошибку в самом расчёте. Состояние — чтением результата командой, а не памятью о117том, что команда вызывалась.118119**Необратимое и критичное — два прохода**, не видящих друг друга. При расхождении побеждает120предъявивший `файл:строка`, вывод команды или число замера, а не более уверенный.121122**Дробить проверку на короткие шаги.** Единственный длинный фоновый проход не переживает123перезапуск инструмента и теряет результат целиком; скриптовые проверки живучее прочих и идут124первыми. Тело правила, таблицы ступеней и разбор — `references/execution-and-sterility.md`.125126## Границы и дисциплина127128**Необратимое — только оператор.** Снятие питания на живой записи, стирание разделов,129перепрошивка без пути отката, любые деструктивные команды прибору — предлагаются, но не130выполняются. Отдельно: если пути отката нет физически (нет резервной копии прошивки,131корпус неразборный) — это стоп, а не «аккуратно попробуем».132133**Диагностическая сборка ≠ релизная.** Всё, что включено ради наблюдаемости, помечается и134в выпуск не уезжает. Обратное тоже верно: измерять надо ту сборку, которую выпускают, иначе135меряется не тот объект.136137**Чужая зона (§ 12).** Прошивку чужого контура не правим: находки, доказательства138`файл:строка`, промпт владельцу. Рецензия чужого аудита — профильная работа139(actor ≠ verifier), делается по исходному коду, а не по пересказу.140141**Замер «до» обязателен.** Сначала телеметрия как есть, потом изменения. Иначе «починили»142неотличимо от «не воспроизвелось» — а это разные вещи с разной ценой.143144## Справочники145146| Файл | Содержимое |147|---|---|148| `references/esp-idf-diagnostics.md` | штатные средства ESP-IDF и FreeRTOS: сторожевые таймеры, дампы, трассировка, метрики кучи и стеков — с источниками |149| `references/flash-storage.md` | тайминги NOR-флеш, поведение файловых систем при обрыве питания и при заполнении, следствия для таймаутов |150| `references/test-methods.md` | методология: длительные испытания, фаззинг, внесение отказов, испытания с оборудованием в контуре — с источниками |151| `references/release-checklist.md` | критерии готовности продукта, чек-лист приёмки, форма отчёта |152| `references/execution-and-sterility.md` | кто исполняет проверки, лестница ступеней, стерильность проходов, живучесть |153| `references/testplan-atomspectra-waterfall.md` | ПРИМЕР полного прогона лестницы 0-8 на реальном проекте (прошивка ESP32 + ПК-приёмник, 2026-08-20) — шаблон структуры плана, переносимые уроки процесса |154| `scripts/aswf/verify_aswf.py` | независимая проверка целостности `.aswf`-файлов (CRC32 построчно) — извлечён из прогона выше, переиспользуемый инструмент |155| `scripts/doc-inventory/docs_inventory.py` | Ollama-инвентаризация md-документации (функции/интерфейсы/форматы/ограничения из README и т.п.) — не проектно-специфичен, годен для любой прошивки с доками |156157## Тест-план: диагностика незнакомой прошивки1581591. Инвентаризация (фаза 0) — карта задач, память, хранилище; отдельно записать, **чего в160 системе нет**: это сокращает работу сильнее, чем любая находка.1612. Прочитать её собственные разборы прошлых отказов, если есть, — чтобы не переоткрывать162 уже починенное, а проверять полноту фиксов.1633. Статика по линзам `code-audit-core`, приоритет — «размер × риск».1644. Приём «бюджет против таймаута» и приём «что делает молча» — оба дают находки до железа.1655. Наблюдаемость (фаза 3) — до первого длительного прогона, без исключений.1666. Базовые метрики, затем нагрузка, хаос, целостность.1677. Приёмка по `references/release-checklist.md`; невыполненные пункты — в отчёт как есть,168 без округления в свою пользу.169170## Анти-паттерны171172| Избегать | Почему |173|---|---|174| Суточный прогон без наблюдаемости | отказ произойдёт и не оставит следов; время потрачено |175| «Наверное, из-за X» без метки [Г] | гипотеза читается как факт и определяет решения |176| Устный счёт бюджета времени | множители путаются; считать скриптом с промежуточными числами |177| Диагностическая сборка в релиз | меняет тайминги; выпускается не то, что измеряли |178| Мерить одну подсистему в изоляции | отказы этого класса живут на стыках, а не внутри узлов |179| Чинить найденное до конца измерений | пропадает база сравнения; сначала зафиксировать «до» |180| «Не воспроизводится» как вывод | это не вывод, а состояние: значит, не хватает наблюдаемости |181| Считать зелёный прогон доказательством | отсутствие отказа за сутки не равно его отсутствию; писать длительность и условия |182183## Связанные скиллы184185- `code-audit-core` — мастер-скилл: линзы, шкала P0–P3, форма отчёта.186- `sci-search` — внешние факты и уровни ✅/📗 для меток [В].187- `~/.claude/references/systematic-debugging.md` (#DBG-1) — один воспроизводимый баг, а не поиск188 неизвестных: сначала причина, потом правка; три неудачных фикса — вопрос к архитектуре.189- `product-dev-cycle` — тест до кода, мутационная приёмка, цикл ветки, приём критики.190- `six-corner-audit` — гейт перед мержем и публикацией.191- `incident-log` — фиксация случившегося отказа и уроков из него.192- `md-doc` — вёрстка отчёта и плана для оператора.