Инструменты
Чистый Python, без зависимостей. Нужны, чтобы не считать пропорции в уме и
чтобы CI ловил опечатки. Полный список команд перед коммитом — в корневом
файле AGENTS.md.
Рецепты лежат в ../recipes/ в JSON, чтобы их можно было читать
и как рецепт, и считать программно.
recalc.py — пересчёт пропорций
python tools/recalc.py recipes/chocolate/dark-70.json # как есть
python tools/recalc.py recipes/chocolate/dark-70.json --scale 2 # вдвое больше
python tools/recalc.py recipes/twix/caramel.json --yield 500 # ровно 500 г карамели
python tools/recalc.py recipes/chocolate/milk.json --table md --share # таблица с долями
python tools/recalc.py --list recipes/ # список рецептов
python tools/recalc.py --validate recipes/ # проверка
echo '{"name":"Тест","ingredients":[{"name":"Мука","amount":100}]}' \
| python tools/recalc.py - --scale 3 # через stdin--yield считает множитель от поля yield (выход готового изделия), а если его
нет — от суммы ингредиентов. У карамели сырья 422 г, а на выходе 300 г, поэтому
«500 граммов» должно означать 500 г готовой карамели, а не сырья.
--table: txt (по умолчанию), md — вставить в документ или issue, csv —
для таблицы. --share добавляет долю каждого ингредиента.
--validate проверяет: битый JSON; пустой или отсутствующий ingredients;
пропущенные name и amount; отрицательные количества; повторяющиеся
ингредиенты с указанием позиций; нулевые количества у всех сразу; расхождение
суммы с yield больше чем на 12 % (считается от суммы; при allow_loss: true
проверка отключается целиком — у карамели и сиропов выпаривание законно).
shopping_list.py — список покупок
Суммирует ингредиенты из нескольких рецептов, убирает дубликаты, показывает из
чего каждый нужен. Для производственного дня —
../docs/base/batch-day.md.
python tools/shopping_list.py \
recipes/chocolate/dark-70.json:4 \
recipes/chocolate/milk.json:3 \
recipes/twix/wafer-short-dough.json:2 \
recipes/twix/caramel.json:2
python tools/shopping_list.py --table md ... # для issue
python tools/shopping_list.py --no-sources ... # компактнееМножитель задаётся через двоеточие: путь:4.
Ингредиент считается обязательным, если он обязателен хотя бы в одном
рецепте: какао-масло необязательно в тёмном шоколаде, но обязательно в молочном —
значит, в список оно попадёт как обязательное. Остальные необязательные
("optional": true) выносятся в раздел «По желанию» и в итог не входят.
Проверки
| Команда | Что ловит |
|---|---|
python tools/check_links.py | Битые относительные ссылки; подсказывает, где файл на самом деле. Внешние ссылки и якоря пропускает. Открытые задачи на ненаписанные разделы помечаются как ошибки — это список дел |
python tools/check_no_emoji.py | Эмодзи в текстах. Правило живёт в AGENTS.md |
python tools/check_alchemy.py | Расхождение связей карты с составом рецептов. Схема рисует утверждение: если она говорит «горький → молочный», читатель поверит и купит горький |
python tools/check_variants.py | Расхождение списка вариантов в data/alchemy.json с блоками ::: variant в статье |
python tools/site/check_site.py | Битые ссылки и ассеты в собранном сайте, остатки markdown, несуществующие docUrl и recipeUrl в данных карты |
python tools/site/verify_build.py | Побайтовую воспроизводимость сборки site/ |
Формат рецепта
{
"name": "Горький шоколад 70 %",
"description": "Базовый рецепт крафта из какао-тертого.",
"yield": 510,
"yield_unit": "г",
"doc": "docs/chocolate/recipes/dark-70.md",
"tags": ["шоколад", "горький"],
"ingredients": [
{ "name": "Какао-тертое", "amount": 350, "note": "негорький сорт" },
{ "name": "Сахар", "amount": 150 },
{ "name": "Какао-масло", "amount": 10, "optional": true }
],
"notes": ["Темперирование: 45-50 °C → 27-28 °C → 31-32 °C"]
}| Поле | Обязателен | Значение |
|---|---|---|
name | да | Название рецепта |
ingredients | да | Список объектов, непустой |
ingredients[].name | да | Название ингредиента |
ingredients[].amount | да | Количество в граммах |
ingredients[].note | — | Пояснение |
ingredients[].section | — | Группировка («Тесто», «Начинка») |
ingredients[].optional | — | Помечает как необязательный |
description | — | Подзаголовок в выводе |
yield | — | Выход по рецепту |
yield_unit | — | Единица выхода, по умолчанию г |
allow_loss | — | Разрешает расхождение суммы с yield больше 12 % |
checked | — | Автор сайта приготовил рецепт, и получилось вкусно. Метка «проверено» в списке |
doc | — | Страница, где расписан рецепт: docs/...md. На неё ссылается панель карты |
tags | — | Для поиска |
notes | — | Заметки в конце вывода |
CI
.github/workflows/ci.yml гоняет все проверки выше
на каждом PR и каждом пуше в main, плюс дымовой тест утилит и побайтовое
сравнение site/ со сборкой из текущих markdown.
Перед коммитом хотя бы:
python tools/recalc.py --validate recipes/ && python tools/check_links.py