Бесплатный интерактивный курс · aicoding.space
Фабрика курсов: package-tutorial-factory
Курс о пакете, которым сделан этот курс. За одиннадцать секций ты пройдёшь весь конвейер фабрики — от разбора пакета до сайта, который проверяют, проходя его кликами, — и соберёшь собственный курс по своему пакету.
Содержание курса
1. Зачем нужна фабрика курсов
Какую проблему решает пакет и почему README недостаточно
Ключевая мысль: фабрика курсов
Привет! Ты держишь в руках инструмент, которым сделан этот самый курс. Да, именно так: страницу, которую ты читаешь, собрал пакет @dzhechkov/skills-tutorial-factory — та самая фабрика курсов, про которую он и рассказывает.
Знакомься: Мира. Она уже прошла курс по dz и курс по конвейеру фич, так что терминал её не пугает. Но сегодня у неё новая беда. Мира написала хороший пакет, приложила README на двадцать экранов — и через неделю выяснила, что коллеги пролистали его один раз и забыли. Документацию читают по диагонали. Курс — проходят.
Что делает фабрика курсов:
- сама разбирает пакет — README, файлы навыков, экспорты, тесты — и составляет список тем;
- пишет курс по методу Head First — с живым персонажем, упражнениями и наградами;
- проверяет результат машиной, а не на глаз: сначала структурный гейт, потом запуск готового сайта.
Главное отличие от «попроси модель написать курс»: метод здесь не зашит в код. Он вынесен в отдельный файл — метод-базу, и каждая секция обязана сослаться на конкретный приём оттуда. Хочешь изменить метод — правь файл, а не программу.
Исходники и публичное зеркало: github.com/djd1m/dz-harness.
Компромиссы: сильная сторона — курс выходит воспроизводимым: те же входные данные дают тот же результат, а проверки ловят пустышки. Слабая сторона — фабрика бесполезна там, где учить нечему: если у пакета нет ни README, ни навыков, она честно откажется, а не выдаст курс из одной страницы.
💬 Просто попроси.
- «Сделай курс по этому пакету» → ассистент вызовет навык package-tutorial-factory и начнёт с разбора твоего пакета.
- «Чем курс лучше README?» → ассистент покажет раздел When to use it и назовёт случаи, где фабрика не нужна.
3. Конвейер из шести шагов и две контрольные точки
Карта пути от пакета до проверенного сайта курса
Ключевая мысль: конвейер из шести шагов
Прежде чем нажимать кнопки, посмотри на карту целиком. Фабрика — это конвейер из шести шагов, и Мира ведёт по нему свой пакет ровно один раз, сверху вниз.
- Извлечение — по пакету собирается бриф: какие темы вообще можно преподавать.
- Написание курса — бриф превращается в объект курса; это единственный шаг, где всерьёз работает модель.
- Структурный гейт — детерминированная проверка без единого обращения к модели.
- Ревью на «дружелюбность к мозгу» — курс читает новый рецензент на другой модели и оценивает тон и историю.
- Рендер — объект курса превращается в один самодостаточный HTML-файл.
- Сдача — итоговая проверка по списку: гейт прошёл, права чисты, метод-база на месте, сайт запускается.
Между шагами стоят две контрольные точки — места, где конвейер останавливается и спрашивает человека:
- confirm-topics — после написания курса: «вот список тем, каждая со своим типом упражнения и ссылкой на приём метода. Согласен?»
- review-course — после рендера: «вот готовый курс, посмотри глазами».
Зачем останавливаться, если всё автоматизировано? Затем, что список тем — это решение о том, чему учить, а машина такие решения принимать не должна. Мира на первой контрольной точке выкинула две темы про устройство сборки: извлеклись они правильно, но новичку не нужны.
Компромиссы: сильная сторона — после гейта и до конца пути модель не вызывается ни разу, поэтому шаги 3, 5 и 6 воспроизводимы и годятся для непрерывной интеграции. Слабая сторона — конвейер линейный: если после рендера понял, что тема лишняя, возвращаешься к написанию курса и проходишь гейт заново.
💬 Просто попроси.
- «Покажи, из каких шагов состоит конвейер» → ассистент откроет карту шагов и объяснит, что даёт каждый.
- «Останови меня перед рендером» → ассистент доведёт до контрольной точки review-course и покажет курс до сборки сайта.
4. Шаг 1: извлечение концептуального брифа
Как пакет превращается в список тем и когда фабрика честно отказывается
Ключевая мысль: концептуальный бриф
Первый шаг вообще не обращается к модели. Скрипт extract-brief.mjs читает документацию пакета и составляет концептуальный бриф — список тем с зависимостями между ними.
Что попадает в бриф:
- каждый содержательный раздел README второго уровня — отдельная тема;
- каждый файл навыка SKILL.md — отдельная тема;
- обзорная тема на весь пакет и, если навыков нет, тема по программному интерфейсу из package.json.
Мира запустила его на своём пакете и получила семь тем. Разбор устроен аккуратно: заголовок внутри блока кода темой не станет, а служебные разделы вроде «Лицензия» и «История изменений» отсеиваются по списку исключений.
Самое интересное — когда фабрика отказывается работать. Решает не число тем, а объём содержательной документации, порог по умолчанию — 1500 знаков (вступление с бейджами в зачёт не идёт). Дальше два исхода:
- документации мало, но кода много → выход 3 и сигнал understand-anything: сначала разбери код другим навыком, его абстракции станут брифом;
- мало и того, и другого → выход 3 и сигнал insufficient-surface: остановиться и честно сказать «учить нечему». Курс из одной страницы не выпускается никогда.
Именно это и было исправлено в версии 0.3.0: раньше пакет без файлов навыков упирался в потолок из двух тем и всегда требовал разбора кода. После правки тот же harness-cli дал 18 тем без всякого отказа.
Компромиссы: сильная сторона — шаг быстрый, бесплатный и повторяемый, и отказ он выдаёт прямо, а не подсовывает тихую пустышку. Слабая сторона — извлекатель режет README только по заголовкам второго уровня, поэтому подтемы внутри больших разделов теряются, и автору курса приходится дочитывать README самому.
💬 Просто попроси.
- «Что вообще можно преподавать по этому пакету?» → ассистент соберёт концептуальный бриф и покажет список тем.
- «Хватит ли документации для курса?» → ассистент запустит извлекатель и скажет, проходит ли пакет порог или нужен разбор кода.
5. Шаг 2: объект курса course.json
Из чего состоит курс и почему каждая секция ссылается на приём метода
Ключевая мысль: объект курса course.json
Второй шаг — единственный, где всерьёз работает модель. На выходе один файл: объект курса course.json. Он же данные для сайта, он же вход для гейта — одна структура на две роли, поэтому расхождению взяться неоткуда.
Что обязано быть внутри:
- language, courseTitle, courseDescription — язык и заголовки;
- persona — один сквозной персонаж на весь курс (у нас это Мира);
- sections[] — минимум три секции, у каждой свой идентификатор, иконка, тип упражнения, ключевое понятие, теория, упражнение, вопрос финального теста и ссылка на приём метода;
- achievements[] — не меньше восьми наград, faqData[] — от пяти до восьми вопросов;
- topics[] — облегчённая проекция секций, которую строит функция toStepZero.
Типов упражнений ровно шесть: quiz, flashcards, matching, drag-and-drop, builder, scenario. Это не украшение, а требование метода: смена вида задания освежает внимание.
Ключевое правило этого шага — тройное кодирование. Одно понятие секции должно встретиться трижды: в теории, в упражнении и в вопросе финального теста. Мира сначала считала это бюрократией, пока не заметила: как только понятие исчезает из упражнения, секция превращается в текст, который читают глазами и не вспоминают.
И ещё одно: каждая секция обязана назвать приём метода, которому она служит — идентификатор вида P1–P12 или D1–D4 из метод-базы. Ссылка проверяется машиной, выдумать несуществующий приём нельзя.
Компромиссы: сильная сторона — контракт жёсткий, поэтому курс проверяем автоматически и его нельзя «почти сделать». Слабая сторона — шаг дорогой: это единственное место, где нужна сильная модель, и именно здесь курс получается живым или скучным.
💬 Просто попроси.
- «Собери курс по моему пакету на десять секций» → ассистент напишет объект курса и покажет список тем на подтверждение.
- «Разнообразь упражнения» → ассистент перераспределит типы упражнений так, чтобы подряд не шли одинаковые.
6. Шаг 3: детерминированный гейт
Тринадцать структурных проверок без единого обращения к модели
Ключевая мысль: детерминированный гейт
Третий шаг — детерминированный гейт: скрипт headfirst-gate.mjs читает объект курса и выносит вердикт. Ни одного обращения к модели, ни случайных чисел, ни текущей даты — поэтому один и тот же курс всегда получает один и тот же ответ, и проверку можно поставить в непрерывную интеграцию.
node "$SKILL_ROOT/scripts/headfirst-gate.mjs" --course course.json --json gate.jsonЧто он доказывает — тринадцать структурных свойств, среди них:
- у каждой секции есть непустое упражнение своего типа (пустая заготовка отвергается);
- подряд не идут три секции одного типа, а при шести и более секциях встречаются все шесть типов;
- понятие секции присутствует в теории, в упражнении и в финальном тесте;
- у каждой секции полная рефлексия: сильные стороны, слабые, оценка и итоговая фраза;
- имя персонажа встречается в каждой секции;
- наград не меньше восьми, все с разными условиями;
- ссылка на приём метода разрешается по метод-базе.
Две детали, которые Мира оценила не сразу. Во-первых, гейт отвергает невидимую пустоту: строка из одних лишь символов нулевой ширины не считается содержанием. Во-вторых, метод-база защищена от подмены: если подсунуть свою базу, где объявлен приём P99, гейт сверит её отпечаток с эталонным и откажется — подделать источник цитат нельзя.
И главное правило работы с гейтом: чинят курс, а не гейт. Красный вердикт — это найденный дефект курса, а не помеха. Мира однажды попробовала ослабить проверку, чтобы «пройти быстрее», и получила курс, где в двух секциях вообще не было упражнений.
Компромиссы: сильная сторона — определённость: свойство либо есть, либо нет, без мнений. Слабая сторона — гейт не судит смысл: он видит, что упражнение непустое, но не видит, что оно бессмысленное.
💬 Просто попроси.
- «Проверь курс гейтом» → ассистент запустит проверку и покажет список несоблюдённых свойств.
- «Гейт ругается на персонажа» → ассистент найдёт секции, где имя персонажа не встречается, и допишет их.
7. Шаг 4: семантическое ревью другой моделью
Кто судит тон, неожиданность и историю — и почему это отдельный слой
Ключевая мысль: семантическое ревью
Гейт сказал «прошло». Значит ли это, что курс хороший? Нет — и фабрика говорит об этом прямо, вместо того чтобы делать вид.
Проверки разнесены по двум уровням. Первый — структурный гейт: правило, определённость, ноль моделей. Второй — семантическое ревью: курс читает независимый рецензент на другой модели — он опирается на метод-базу и оценивает то, что правилом не решается:
- тон — обращаются ли к читателю на «ты», живая ли речь;
- неожиданность — есть ли поворот, шутка, момент «ага»;
- история — тянется ли сквозь курс сюжет с персонажем.
Скрипт brain-friendliness-prompt.mjs собирает для рецензента запрос, опирающийся на метод-базу. Внутрь встроены два правила честности:
1. Пустой или безоценочный ответ — это громкий отказ, а не чистое прохождение. Молчание рецензента никогда не считается похвалой.
2. Ревью советует, а не запрещает. Оно не блокирует выпуск автоматически, но конвейер считает курс готовым только после того, как ревью состоялось.
Почему рецензент — обязательно другая модель? Потому что тот, кто писал текст, читает его как автор: он видит замысел, а не то, что написано. Мира убедилась на своём курсе — её собственная вычитка нашла две шероховатости, чужая модель нашла девять, включая целую секцию, где персонаж исчезал ровно там, где становилось скучно.
Компромиссы: сильная сторона — ловится ровно то, чего правило не поймает никогда, и делается это независимым читателем. Слабая сторона — оценка модельная, значит нестабильная: два прогона могут разойтись, и это цена за суждение вместо правила.
💬 Просто попроси.
- «Проверь курс на живость» → ассистент соберёт запрос для семантического ревью и отправит его другой модели.
- «Рецензент ответил пусто» → ассистент сообщит об отказе, а не запишет молчание в успех.
8. Шаги 5–6: рендер и верификатор, который проходит курс
Один HTML-файл — и проверка не текста, а поведения
Ключевая мысль: верификатор проходит курс кликами
Вот место, где Мира удивилась сильнее всего.
Сначала обычное: render-site.mjs берёт прошедший гейт объект курса и собирает один самодостаточный HTML-файл. Он открывается прямо с диска, без сервера и без сети, и сборка детерминирована — тот же курс даёт те же байты.
node "$SKILL_ROOT/scripts/render-site.mjs" --course course.json --out site/index.html
node "$SKILL_ROOT/scripts/verify-site.mjs" --site site/index.htmlА теперь неожиданное. Вторая команда не разбирает страницу и не ищет в ней слова. Верификатор проходит курс кликами: он запускает встроенный в страницу код в своей среде, открывает каждую секцию, выполняет каждое упражнение нажатиями, сдаёт финальный тест, жмёт «Сброс» — и после этого проверяет *сохранённое состояние*, а не текст на экране. Двадцать девять поведенческих проверок, выход 0 только если сошлись все.
Разница принципиальная. Проверка текста говорит «в HTML есть слово „пройдено“». Проверка поведения говорит «упражнение действительно засчиталось, а кнопка сброса действительно обнулила прогресс». Именно так нашлась ошибка, при которой «Сброс» ничего не делал: разметка была безупречной, поведение — сломанным.
Ещё две мелочи, которые видно на готовом сайте:
- подвал со ссылками — по умолчанию каналы мастерской, переопределяется полем footer.links; допускаются только адреса https, а подвал, из которого отфильтровались все ссылки, роняет проверку громко, а не исчезает молча;
- язык интерфейса — все служебные надписи лежат в одной таблице и выбираются по полю language, поэтому русский курс и читается, и проверяется по-русски.
Граница доверия — о ней надо знать. Верификатор исполняет код из переданного ему HTML. Это не песочница для чужих страниц: подавай ему только то, что собрала эта фабрика или другой доверенный источник.
Компромиссы: сильная сторона — зелёный результат означает «курс работает», а не «разметка валидна». Слабая сторона — цена в доверии: раз код исполняется, вход обязан быть проверенным.
💬 Просто попроси.
- «Собери сайт курса и проверь его» → ассистент выполнит рендер и запустит верификатор, показав число сошедшихся проверок.
- «Поменяй ссылки в подвале» → ассистент пропишет footer.links и напомнит, что принимаются только адреса https.
9. Права на книгу: что уезжает в пакет, а что остаётся дома
Метод-база, проверка на дословные совпадения и слоистая защита
Ключевая мысль: проверка на дословные совпадения
Метод Head First взят из книги, а книга защищена авторским правом. Как отдать метод и не отдать книгу?
Ответ фабрики — разделить две вещи. Методы и факты не охраняются авторским правом, охраняется их выражение. Поэтому наружу уезжает только пересказ приёмов: файл head-first-method.md, где каждый приём сформулирован своими словами и снабжён ссылкой на страницу первоисточника. Сам оцифрованный текст книги остаётся на машине, в git не попадает и в пакет не кладётся.
Защита сделана слоями, а не одним замком:
1. Автор пишет из очищенной базы. Модель видит пересказ, а не книгу, поэтому дословному тексту неоткуда взяться.
2. Корпус исключён из пакета структурно — список файлов пакета просто не содержит этих путей, это проверяется отдельным тестом.
3. Проверка на дословные совпадения. Скрипт shingling-check.mjs ищет любые совпадающие цепочки длиной от восьми слов между курсом и корпусом книги. Ноль совпадений — обязательное условие.
Мира спросила ровно то, что стоит спросить: «а если кто-то нарочно спрячет текст — переставит буквы, вставит невидимые символы?» Ответ записан в документации честно: это прямо объявлено вне области действия. Проверка защищает от обычного, не злонамеренного переиспользования; она не средство защиты от подделки и не судья.
Компромиссы: сильная сторона — три независимых слоя, и каждый работает сам по себе, поэтому пробой одного не открывает дверь. Слабая сторона — против целенаправленного обхода слои не рассчитаны, и об этом сказано вслух, а не замолчано.
💬 Просто попроси.
- «Проверь курс на дословные заимствования» → ассистент запустит проверку по корпусу и покажет результат.
- «Что из книги попадает в опубликованный пакет?» → ассистент покажет метод-базу и объяснит, почему корпус остаётся локальным.
10. Честные границы обещаний
Чего проверки не доказывают — и почему это написано прямо в README
Ключевая мысль: честные границы обещаний
Самый необычный раздел документации этого пакета называется «Область действия и честные ограничения». В нём написано, чего проверки не доказывают. Мира сначала решила, что это признание слабости. Потом поняла, что наоборот.
Вот эти честные границы обещаний — почти дословно:
- Проверки первого слоя доказывают структуру и чистоту прав, и только их: наличие, непустоту, разрешимость ссылок на метод, отсутствие дословных заимствований в тексте, написанном без злого умысла.
- Намеренно сделанный курс-пустышка структурный гейт пройти МОЖЕТ. Ловит его семантическое ревью — и поэтому конвейер требует, чтобы ревью состоялось до того, как курс считается готовым.
- Осмысленность и «настоящий ли это Head First по голосу» — свойство второго слоя, не первого.
- Подпись пакета удостоверяет собранный архив, а не отдельно скопированные файлы: каталог исходников считается неудостоверенным, пока архив не пересобран и не проверен.
Почему так написано? Потому что документированная граница — это защита от худшего вида ошибки: когда проверку считают сильнее, чем она есть, и перестают смотреть глазами. Гейт, про который думают «он ловит плохие курсы», опаснее отсутствия гейта: он раздаёт ложное спокойствие.
Есть и техническая причина сдержанности. Каждый раунд усиления проверок закрывал реальную дыру — и каждый же обнаруживал следующую, более хитрую. Это признак бесконечной погони: «осмысленно ли написано» правилом не решается в принципе. Разумный ход — назвать границу и поставить над ней второй слой, а не обещать невозможное.
Компромиссы: сильная сторона — читатель точно знает, на что опирается, а на что нет. Слабая сторона — честный список ограничений выглядит скромнее рекламного «полная проверка качества», и это осознанная плата.
💬 Просто попроси.
- «Что гейт НЕ доказывает?» → ассистент откроет раздел ограничений и перечислит границы по пунктам.
- «Можно ли считать зелёный гейт знаком качества?» → ассистент объяснит разницу между структурой и смыслом и напомнит про второй слой.
11. Собери собственный курс по своему пакету
Финальная сборка: решения принимаешь ты, конвейер только исполняет
Ключевая мысль: собственный курс по своему пакету
Последний шаг — твой. Мира свой курс уже собрала; теперь ты соберёшь собственный курс по своему пакету, и решения на развилках будут твоими.
Полный путь от начала до конца:
- Установи навык:
dz init --target claude-code --select package-tutorial-factory. - Извлеки бриф:
node "$SKILL_ROOT/scripts/extract-brief.mjs" --pkg <каталог пакета> --json brief.json. - Напиши объект курса и подтверди список тем на контрольной точке.
- Прогони гейт:
node "$SKILL_ROOT/scripts/headfirst-gate.mjs" --course course.json --json gate.json. - Отдай курс на семантическое ревью другой модели.
- Собери и проверь сайт:
render-site.mjs, затемverify-site.mjs.
Три решения, которые никто за тебя не примет:
- Сколько секций. Извлекатель показывает всё, что нашёл; сколько из этого нужно новичку — вопрос к автору. Мира из семи тем сделала одиннадцать секций, разбив две крупные.
- Кто персонаж. Одно имя на весь курс, и оно обязано встречаться в каждой секции — иначе история рвётся ровно там, где становится трудно.
- Где остановиться. Иногда честный ответ — «учить нечему»: тогда пиши указатель в файле навыка, а не курс.
И правило, которое стоит унести с собой целиком: чинят курс, а не проверку. Красный гейт — это подарок, найденный дефект. Ослабленный гейт — это тот же дефект, только теперь невидимый.
Компромиссы: сильная сторона — путь короткий, и после написания курса всё повторяемо. Слабая сторона — качество упирается в один шаг: сколько вложишь в написание курса, столько и получит читатель, а конвейер лишь не даст выпустить сломанное.
💬 Просто попроси.
- «Сделай курс по моему пакету на десять секций» → ассистент пройдёт весь конвейер и остановится на контрольных точках.
- «Проверь мой курс целиком перед публикацией» → ассистент прогонит гейт, проверку прав и запуск сайта и покажет сводку.
Частые вопросы
- Нужно ли ставить весь harness, чтобы пользоваться фабрикой курсов?
Нужен dz — через него навык раскладывается в формат твоего агента: npm i -g @dzhechkov/harness-cli, затем dz init --target claude-code --select package-tutorial-factory. Сами скрипты фабрики зависимостей не требуют: только Node и встроенные модули.
- Курс обязательно должен быть на русском?
Нет. Язык задаётся полем language объекта курса. Значение ru включает русские надписи интерфейса целиком; любое другое значение оставляет английские. Названия команд не переводятся ни при каком языке.
- Сколько секций должно быть в курсе?
Минимум три по контракту, разумный рабочий диапазон — восемь–двенадцать. При шести и более секциях гейт требует, чтобы встречались все шесть типов упражнений, поэтому слишком короткий курс теряет разнообразие.
- Что делать, если извлекатель отказался работать?
Смотри сигнал: understand-anything означает «документации мало, но код большой» — разбери код соответствующим навыком и используй его абстракции как бриф. insufficient-surface означает «учить нечему» — остановись и скажи это честно, курс из одной темы не выпускается.
- Гейт зелёный — значит курс хороший?
Нет. Зелёный гейт означает, что выполнены структурные свойства: упражнения непустые, понятия повторены трижды, персонаж есть в каждой секции, ссылки на метод разрешаются. Живость текста оценивает семантическое ревью другой моделью, и конвейер считает курс готовым только после него.
- Можно ли изменить сам метод обучения?
Да, и для этого не надо трогать код: метод лежит в файле head-first-method.md, приёмы P1–P12 и D1–D4 берутся оттуда во время работы. Правишь файл — меняется метод. Но гейт сверяет отпечаток базы с эталонной, поэтому подменить её на ходу, чтобы прошли выдуманные ссылки, не получится.
- Безопасно ли запускать верификатор на чужом HTML?
Нет. Верификатор исполняет код из переданной страницы в своей среде — это проверка поведения, а не песочница. Подавай ему только то, что собрала эта фабрика или другой доверенный источник.