Imported from TP-Prepare/review-frontend-homework-skill (
skills/review-homework/SKILL.md). Install upstream withnpx skills add TP-Prepare/review-frontend-homework-skill --skill review-homework. Copyright stays with the author.
Ревью домашки по фронтенду
Готовит ментору черновик ревью студенческого PR и постит его только после подтверждения.
Когда применять
Ментор просит проверить PR в frontend-park-mail-ru/homework_2026_2
(или в репозитории другого потока — homework_2026_1, homework_2024_2).
Обычно звучит как «отревьюй PR 4», «проверь домашку Иванова», «глянь мои PR».
Железные правила
- Ничего не публикуется в GitHub без явного подтверждения ментора. Ревью, комментарии, лейблы и резолвы тредов — только после «да». Единственные записи до Фазы 4 — ассайн ментора на себя в Фазе 0 и, симметрично, его откат вместе с лейблом «На проверке», если ментор откажется от постинга; обе делаются по подтверждению. Всё остальное в Фазах 1–3 только читает GitHub и печатает в терминал; писать эти фазы могут лишь во временные файлы в скретчпаде.
- Никогда не ставить лейблы
5/5иСписано. Финальный балл — за старшим ментором, списывание — не наша зона. - Никогда не пушить в ветку студента и не мержить PR.
- Тон — сократический. Мы спрашиваем, а не приказываем. Студент должен
понять почему, а не просто применить патч. Формулировки — из
references/comment-bank.md. - Один блокирующий уровень за раз. Если PR оформлен неправильно (не та базовая ветка, тронуты запрещённые файлы) — новый проход по уровням B и C не начинаем, просим переоформить. Это не касается уже открытых тредов прошлого ментора по коду и тестам — их статус (исправлено / не исправлено / студент ответил вопросом / вопрос без ответа) всё равно определяем в Фазе 2.
- Инлайн-заметка обязана требовать действия или ответа. Каждый инлайн-комментарий в GitHub заводит тред с кнопкой «Resolve conversation» и попадает в счётчик нерешённых обсуждений, по которому ментор и студент отслеживают, что осталось. Поэтому инлайном идёт только то, что несёт замечание или вопрос. Чистая похвала треда не оправдывает — ей место в сводном комментарии. Похвала с вопросом («Молодец, что использовал стрелочные функции) Расскажи об отличиях от обычных?») — полноценная заметка, её оставляем инлайном.
Фаза 0 — Старт
Единственная фаза до Фазы 4, которой разрешена запись — ассайн и, если ментор
передумает постить, его откат вместе с лейблом, — и обе делаются по
подтверждению. Команды — в references/gh-recipes.md, секция «Старт».
Собрать до всякого чтения диффа:
- Свой логин (
ME) — иначе нельзя отличить свой PR от чужого - Владельца PR:
assigneesиreviewRequests - Имя и контакт ментора из его прошлых ревью в этом репозитории
Определить статус PR:
| Состояние PR | Статус | Что делаем |
|---|---|---|
| Ни ассайна, ни ревьювера | свободен | берём |
| Все непустые логины — свои | твой | ты подхватил его раньше сам; идём дальше без тревоги |
| Хоть один чужой логин | чужой | вопрос про перехват задаём в той же остановке ниже; перехватываем только после отдельного «да» на него |
Половинчатый жест (ревьювер есть, ассайна нет) — тоже «взят»: ментор потока, подхватывая PR, ставит себя и ревьювером, и ассайни. Чужой логин перевешивает свой. Любой запрошенный ревьювер или ассайни, кроме автора PR, считается ментором.
Затем одна остановка — напечатать ментору и ждать ответа:
- какой PR берём, чей он сейчас; при статусе «чужой» — здесь же, в этой единственной остановке, отдельное согласие на перехват, без второй паузы
- имя и контакт, которые уйдут в знакомство (если нашлись — на подтверждение, если нет — спросить)
- что произойдёт после «да»: ассайн и лейбл «На проверке»
После подтверждения — ассайн, перечитать лейблы. Если автоматика не повесила
«На проверке» — поставить лейбл вручную, рецепт «Ассайн» в
references/gh-recipes.md. Только тогда Фаза 1.
Если ментор в итоге откажется от постинга — откатить только то, что сам скилл поставил в этом прогоне: ассайн и лейбл «На проверке», рецептом «Откат ассайна». Это касается только статуса свободен — PR в этом прогоне взял сам скилл. При статусе твой ассайн не трогать: его поставил ментор ещё до запуска, и отпускать его — его решение. Лейбл при этом статусе снимается ровно в одном случае: если «На проверке» не было и его в этом прогоне повесил сам скилл вручную, потому что в потоке нет автоматики. PR, помеченный как взятый, но без ревью, блокирует его для других менторов.
Фаза 1 — Сбор контекста
Ничего не пишет. Команды — в references/gh-recipes.md, секция «Сбор».
Собрать:
- Метаданные PR: номер, заголовок, автор,
baseRefName,headRefName - Список изменённых файлов с числом добавлений и удалений
- Полный diff
- Статус CI (
eslint --max-warnings 0иkarma); при падении — лог упавшего шага - Номер варианта из базовой ветки → задание из README этой ветки
- Существующие ревью-комментарии и ответы студента — обязательно, иначе повторное ревью будет дублировать закрытые замечания
Владелец PR и лейблы уже собраны в Фазе 0, повторно не запрашиваются.
Определить, первое это ревью или повторное: если в PR уже есть комментарии от менторов, это повторное.
Фаза 2 — Проверки
Читать references/checklist.md целиком и references/variants.md — только
секцию нужного варианта.
Три уровня в строгом порядке:
- A. Блокирующие — оформление PR. Если сработал хоть один из A1, A3, A4, A5, A6 — уровни B и C пропускаем: код ревьюить бессмысленно, пока diff показывает не то. A2 (название PR) не блокирует — сообщается вместе с обычным ревью кода.
- B. Код — реализация функции.
- C. Тесты — дописанные студентом тесты.
При повторном ревью для каждого прошлого замечания определить: исправлено /
не исправлено / студент ответил вопросом / вопрос без ответа. Последний
статус — тред ментора с теоретическим вопросом («зачем 'use strict'»,
«почему объект, а не Map»), на который студент не ответил и код не менял.
Старший ментор такие треды находит и возвращает PR: «а ответ так и не
написал». Не поднимать заново то, что закрыто.
Фаза 2.5 — Проверка запуском
Обязательна, пропускать нельзя. Догадка о поведении кода — не находка.
Репозиторий не клонируем: в песочнице клон упирается в права. Копируем реализацию студента из диффа в файл в скретчпаде и дописываем к ней проверки.
Что прогнать:
- Негативный вход из C1 —
null,undefined, строка, число, объект вместо массива. Записать, что именно происходит: бросок наружу, тихое пустое значение, зависание - Граничные случаи из C2 и специфику варианта из
references/variants.md - Значения, которые функция может не ожидать:
Date,Map,Set,RegExp, функция — если по смыслу варианта они могут прилететь - Циклические ссылки не проверяем и в заметки не выносим. Это вне области
ДЗ0. Ни в одном варианте задание их не требует, а защита от них стоит дорого:
в
deepClone, например, копию надо регистрировать до спуска в детей, то есть отказаться и отvalue.map(deepClone), и отObject.fromEntriesв пользу изменяемого накопления, аWeakMapпротащить служебным параметром в публичную сигнатуру. Просить студента разменять читаемое решение на это радиRangeErrorна кейсе, которого нет в условии, — плохая сделка - Сломать проверяемое место и убедиться, что тест студента краснеет. Зелёный тест на сломанной реализации означает, что тест ничего не проверяет, и это находка уровня C
- Если претензия к скорости — замерить, а не прикинуть на глаз
Правило формулировки: утверждать фактом можно только прогнанное. Всё остальное идёт вопросом («что вернёт функция вот тут?»), а не утверждением («функция вернёт X»).
У каждой проверки должен быть контрольный образец. Прогон умеет врать, и врёт он правдоподобно. Четыре способа обмануться, все четыре случались на живых PR:
- Пустой вход дал пустой выход.
plainify(Object.create(null))вернул{}, и это выглядело как «объект отсекается guard'ом» — а объект просто был пустой. Лечится контрольным прогоном на том же типе входа, но с полями - Сравнение с литералом, полученным тем же способом.
factorial(21)совпал с числовым литералом51090942171709440000— оба парсятся в один и тот же double, проверка кольцевая. Эталон берётся другим механизмом:BigInt, ручной расчёт, другая реализация - Смоделированная семантика вместо настоящей.
assert.throwsпри «не бросила вовсе» проваливается, а моя модель считала это успехом, и вывод получился обратный. Если моделируешь чужой инструмент — сначала прогони модель на заведомо проходящем и заведомо падающем случае - Грепом по CRLF-файлу искали «строку из одних пробелов». Шаблон
^[[:space:]]\+$дал 30 совпадений. Настоящих — ноль:[[:space:]]включает\r, файл на CRLF. Шаблон под\rне подбирать — снимай его:tr -d '\r' < файл | grep -n '^[[:blank:]]\+$'. Сам способ сначала прогони на файле, где строка заведомо есть: ноль на нём значит «шаблон не ловит», а не «строк нет»
Прогоняй сломанный и рабочий вариант рядом. Одиночный результат без пары не доказывает ничего.
Фаза 3 — Черновик в терминал
Напечатать ментору:
-
Шапка — PR, автор, вариант, задание одной строкой, статус CI, какое это ревью по счёту, статус владельца из Фазы 0 (свободен / твой / чужой). «Чужой» определяется сверкой логинов с
ME, а не на глаз: событиеreview_requestedот твоего же аккаунта — это ты, а не другой ментор -
Статус предыдущих замечаний — только при повторном ревью: для каждого — исправлено / не исправлено / студент ответил вопросом / вопрос без ответа
-
Блокирующие проблемы — если есть
-
Покрытие тестами — заполняется всегда, даже когда с тестами всё хорошо. Таблица «кейс → кто закрыл»: базовый тест из ветки варианта / студент / нет. Строки берутся из C0 в
references/checklist.md. Если хоть один негативный кейс оказался в колонке «нет» — заметка по C1 обязательна, это не на усмотрение. Пропущенное покрытие — самая частая находка, которую ревьюер теряет: замечания к коду выглядят содержательнее и вытесняют её -
Обязательные три вопроса — идут инлайном в каждом первом ревью, всегда, независимо от того, всё ли в этих местах в порядке:
'use strict'— B2- способ объявления функции, declaration против expression — B5
- перенос строки в конце файла — B9
Если у студента всё сделано верно, вопрос не отменяется, а меняет форму: становится похвалой с вопросом («перенос на месте 👍 расскажи, зачем он нужен»). Это проверка понимания, а не поиск дефекта, поэтому «тут и так хорошо» — не причина промолчать. Направление вопроса определяется фактами из Фазы 2, а не догадкой: перенос строки проверяется по B9,
'use strict'— первой строкой обоих файлов -
Инлайн-заметки — каждая в формате
путь/файл.js:строка+ текст комментария. Только замечания и вопросы: чистая похвала сюда не идёт (правило 6) -
Сводный комментарий — то, что уйдёт в тело ревью. Сюда же собирается похвала за то, что студент сделал хорошо. В первом ревью открывается знакомством с именем ментора и контактом из Фазы 0 — формулировка в
references/comment-bank.md, секция «Сводный комментарий». При повторном ревью знакомство не повторяется, а в закрывающей строке контакт не дублируется -
Предлагаемый вердикт —
COMMENT/APPROVEи лейбл. Пока есть хоть один тред со статусом «вопрос без ответа», вердикт —COMMENTи лейбл «Нужны исправления», а в сводке — список вопросов, на которые ждём ответ. «ОК от ментора» с неотвеченным вопросом не предлагается: старший ментор вернёт PR за это -
Что скилл сделает после подтверждения — списком
Затем остановиться и ждать. Не постить, не спрашивать «продолжать?» в той же реплике, где печатается черновик — ментор должен успеть прочитать и поправить.
Фаза 4 — Постинг
Только после явного «да, постим» (или после правок ментора).
Порядок — в references/gh-recipes.md, секция «Постинг»:
-
Ассайн уже сделан в Фазе 0 — здесь ничего не ассайним. Если Фаза 0 почему-то не проходила (ментор запустил постинг отдельно), вернуться к ней: PR должен быть взят до публикации, а не после
-
Одно ревью с инлайн-комментариями через
gh api --input. Если ревью уже отправлено и надо добавить одну заметку — одиночный инлайн-коммент, а не второе ревью; если студент ответил вопросом — ответ в существующем треде. Оба рецепта вreferences/gh-recipes.md -
Лейблы: снять неактуальные, поставить нужный — статусный лейбл ровно один
-
Резолв тредов — при повторном ревью обязателен. Каждый тред, по которому студент исправил код или дал верный ответ, закрываем через
resolveReviewThread; рецепт вreferences/gh-recipes.md. Статус берётся из Фазы 2, где он уже определён.Решение принимается только по просьбам самого треда. Выполнены они — резолвим, и неважно, что рядом появилась новая заметка про то же место или тот же файл. Новое замечание живёт в своём треде; держать выполненное открытым «за компанию» — то же самое, что забыть зарезолвить: студент перечитывает просьбу, которую уже закрыл.
Треды с незакрытыми вопросами не трогаем: по счётчику нерешённых обсуждений студент и ментор видят, что осталось, и лишний резолв обесценивает этот счётчик ровно так же, как забытый. Если просьба треда выполнена наполовину — тред остаётся открытым. Чужие треды — только после отдельного подтверждения
-
При вердикте «ОК от ментора» — дополнительно assign
rissenberg(старший ментор потока — Костя Горшков)
После — напечатать ссылку на PR и что именно поставлено.
Справочники
| Файл | Когда читать |
|---|---|
references/gh-recipes.md |
Фазы 0, 1 и 4 |
references/checklist.md |
Фаза 2, целиком |
references/variants.md |
Фазы 2 и 2.5, только секцию нужного варианта |
references/comment-bank.md |
Фаза 3, при формулировании; знакомство — в первом ревью |