diff --git a/docs/reviews/CODE-REVIEW-643-r1.md b/docs/reviews/CODE-REVIEW-643-r1.md new file mode 100644 index 00000000..0cbe377e --- /dev/null +++ b/docs/reviews/CODE-REVIEW-643-r1.md @@ -0,0 +1,160 @@ +# CODE-REVIEW-643-r1 + +Issue: #643 · Этап: code · Заход: r1 · SHA материала: `92b83d9525feec4a16764fdcb041e147709097d8` +Дерево (`git cat-file -p HEAD`): `tree 466e511b3ef0e4e3bc86926173e4fca2d2d11631` — совпадает с +блоком «Материал раунда» хендоффа автора; все перечисленные там blob-хэши сверены +командой `git rev-parse HEAD:` и совпали (`scripts/rebase-generated.mjs`, +`test/rebase-generated.test.mjs`, `.github/workflows/process.yml`, +`scripts/merge-candidate.mjs`, `scripts/rebase-on-dev.mjs`, `scripts/mutation-registry.mjs`). + +## Скоуп + +Дефект #635/#643: конвейер и `merge-candidate.mjs` возвращали зелёные задачи в +`S6-in-progress`, если doc-коммит ветки конфликтовал с `dev` **только** в +генерируемом `docs/reviews/INDEX.md` (24.09 так отскочили #617, #618, #629, +#642×2, #631×2). Правка — общий помощник `scripts/rebase-generated.mjs`: +`planStop`/`rebaseRegenerating` пересобирают индекс по каталогу на остановке, +если все конфликтующие пути — индекс (или объявлены вызывающим, как бандл в +`rebase-on-dev.mjs`); любой другой путь — прежний отказ с полным перечнем. +Задействовано в трёх местах: `process.yml` (шаг «Привести ветку к dev»), +`merge-candidate.mjs` (`rebaseOnto`), `scripts/rebase-on-dev.mjs`. + +Класс изменения: **B** (`scripts/**`, `test/**`, `.github/**`) + **C** +(`PROCESS.md`, `AGENTS.md`) — ни одного файла класса A, инфраструктурный трек, +ускоренный вход по §1 подтверждён. `src/**` и `custom_components/**` не +затронуты — гейты `check-docs.mjs`, golden, pytest, HA-харнесс, invariants, +performance к этой задаче неприменимы (AC не называет ни одного из них). +User-Visible: no — правок в changelog не требуется и не сделано, трейлеры +`Issue: #643` / `User-Visible: no` на коммите на месте. + +## Как проверялось + +Дешёвые гейты (`tsc`, `npm test`, `npm run build` со сверкой копий бандла) +подтверждены зелёным Validate на этом SHA: +https://github.com/Matysh/houseplan-card/actions/runs/35966022704 — не +перегонялись повторно (см. «Дешёвые гейты уже подтверждены» в задании). + +Дополнительно (не входит в дешёвый набор, но обязательно для защитных AC и +границ дельты) прогнано мной лично на рабочей копии `92b83d95`: + +| Гейт | Команда | Результат | +|---|---|---| +| Целевые unit-тесты правки | `node --test test/rebase-generated.test.mjs test/rebase-on-dev.test.mjs test/merge-candidate.test.mjs` | 34 pass, 0 fail | +| Мутант AC2 (mixed-conflict) | `node scripts/mutation-gate.mjs --id=rebase-index-resolves-mixed-conflicts` | «поймано 1 из 1» | +| Мутант AC1 (--ours вместо пересборки) | `node scripts/mutation-gate.mjs --id=rebase-index-takes-a-side` | «поймано 1 из 1» | +| Мутант «сбой пересборки не абортит» | `node scripts/mutation-gate.mjs --id=rebase-index-failure-leaves-half-rebase` | «поймано 1 из 1» | +| Мутант «отказ выглядит как крах» | `node scripts/mutation-gate.mjs --id=rebase-index-refusal-exits-like-a-crash` | «поймано 1 из 1» | +| Мутант AC1 слияния (голый rebase) | `node scripts/mutation-gate.mjs --id=merge-rebase-refuses-index-conflict` | «поймано 1 из 1» | +| Мутант AC3 (`rebase-on-dev` не пересобирает) | `node scripts/mutation-gate.mjs --id=rebase-on-dev-stages-index-unrebuilt` | «поймано 1 из 1» | +| Целостность реестра мутантов (все 894 якоря, включая 6 новых) | `node scripts/mutation-gate.mjs --check` | exit 0, все `ok` | +| Дисциплина коммитов/трейлеров/веток диапазона | `node scripts/process-gate.mjs` | «гейт пройден, предупреждений 0» | +| Windows-переносимость, свежесть индекса, охрана review-doc, монолит-якоря | `node --test test/windows-portability.test.mjs test/reviews-index.test.mjs test/review-doc-guard.test.mjs test/monolith-text-anchors.test.mjs` | 83 pass, 0 fail | +| Модельные входы (не относится к диффу, но дёшево) | `node scripts/check-inputs.mjs --coverage` | exit 0 | + +Каждый из 6 заявленных мутантов проверен лично мной: команда +`mutation-gate.mjs --id=…` запускает названный тест на чистом дереве (проходит) +и на пропатченном (краснеет) — «тест умеет падать» подтверждено исполнением, а +не поверено на слово. + +## Разбор по AC + +| AC | Чем доказан | Чем краснеет | Вывод | +|---|---|---|---| +| AC1: конфликт только в `INDEX.md` ребейзится без отказа, итог = пересборка по каталогу | `rebase-generated.test.mjs` «#643 AC1» (реальный git, временный репозиторий, индекс ≠ dev ≠ ветки, = `buildIndex`); «process.yml на настоящем bash: конфликт только в индексе» (исполняет сам текст шага bash'ем); `merge-candidate.test.mjs` «#643 AC1» (предусловие голого `git rebase` действительно конфликтует, итог — `push` через Validate, не S6, коммиттер `claude[bot]`) | `rebase-index-takes-a-side`, `merge-rebase-refuses-index-conflict` — оба лично прогнаны, «1 из 1» | Доказан исполнением, тесты прошли на моём прогоне | +| AC2: конфликт в любом другом пути (в т.ч. вместе с индексом) — прежний отказ с перечнем | «#643 planStop» (юнит), «#643 AC2» (дерево/HEAD как были, ребейз не брошен), «#643 CLI» (код 3, пути построчно в stdout), «process.yml на настоящем bash: индекс + другой файл» (`conflict=true`, оба пути), «сбой помощника (Node вышел с 1)» → ошибка шага, не conflict | `rebase-index-resolves-mixed-conflicts`, `rebase-index-refusal-exits-like-a-crash`, `rebase-index-failure-leaves-half-rebase` — все три лично прогнаны, «1 из 1» | Доказан исполнением | +| AC3: `rebase-on-dev.mjs` разрешает `INDEX.md` так же | `rebase-on-dev.test.mjs` «#643 AC3: бандл и INDEX.md конфликтуют в одном коммите» (бандл собран из ветки, индекс = пересборка каталога); `splitConflicts` → `regenerated` | `rebase-on-dev-stages-index-unrebuilt` — лично прогнан, «1 из 1» | Доказан исполнением | + +Разбор кода вне AC (проверено чтением, не исполнением, если не указано иное): + +- `planStop`/`rebaseRegenerating` — чистая функция плюс git-цикл; пропуск + пустого коммита через `--skip` (диагностировано чтением + подтверждено + отдельным тестом «индекс-коммит, ставший пустым…», исполнением зелёный). +- Исключение посреди ребейза (например, крах `reviews-index.mjs`) отменяет + ребейз и пробрасывается наверх; в `merge-candidate.mjs` это ловится верхним + `.then(_, err => …)` — уже существующий инвариант «после прогона метка + меняется всегда», не задет правкой (проверено чтением, `mergeCandidate` + вызывается как `async`-функция, чьё исключение синхронного `ops.rebaseOnto` + становится отклонённым промисом). +- `CONVEYOR_IDENTITY` вынесен в единственный источник (`reviews-index.mjs`), + дублирующего литерала `user.name=claude[bot]` в `scripts/`/`.github/` не + осталось — проверено `grep` по всему дереву. +- Импорт помощника в `process.yml` берётся из `origin/dev` (`git archive`), а + не из отставшей ветки — обоснованно (ветка ещё не несёт новый файл) и + закрыто отдельным тестом на замыкание импортов, не выходящее из `scripts/`. +- Хронология «после слияния — зеркало `process.yml` в `main`» не создаёт + цикла «курица-яйцо»: до зеркалирования старый `process.yml` в `main` + поведения не меняет; после зеркалирования `scripts/rebase-generated.mjs` уже + есть в `dev` (слит этим же коммитом) — проверено чтением обоих файлов и + логики шага. +- Одно число — один источник: единственная содержательная константа диффа — + `MAX_STOPS = 500`, объявлена и используется один раз + (`scripts/rebase-generated.mjs:38,111`); дублирования нет. + +## Находки + +Блокирующих находок нет. Одна находка Low, снята без правки: + +- **Low (снята):** `PROCESS.md` абзац «Индекс документов ревью» (строки + 368–369) — фраза «Правка `docs/reviews/`\nруками —…» разбита переносом + строки посреди предложения из-за точечной правки текста. Косметика: в + отрендеренном markdown это один абзац, смысл не искажён, отдельного цикла + не стоит. Снимаю без возврата автору. + +## Что проверено и корректно + +- Все AC доказаны исполнением тестов на реальном `git` во временных + репозиториях, включая прогон самого текста bash-шага `process.yml` + (`spawnSync('bash', …)` на срезе шага) — не имитация, а фактическое + поведение конвейера. +- Все 6 новых мутаций реестра лично проверены: ловятся названным тестом и + только им. +- Трейлеры `Issue: #643` / `User-Visible: no` корректны, changelog не нужен. +- Дерево HEAD и blob-хэши файлов точно совпадают с материалом раунда, + указанным автором. +- `process-gate.mjs` и полный `mutation-gate.mjs --check` (894 якоря) зелёные. +- Документация (`PROCESS.md`, `AGENTS.md`) согласована с кодом; путь + `docs/reviews/INDEX.md` и имя помощника совпадают в обоих местах. + +## Чего не проверял + +- Живой прогон обновлённого `process.yml` в GitHub Actions — шаг исполнен + только локальным `bash -eo pipefail` на временном репозитории (как и указал + автор); реальный `git archive`/`tar` на раннере, права токена и REST-часть + шага (`--force-with-lease`, ожидание ссылки) не пере-исполнялись — они не + тронуты диффом построчно и покрыты прежними тестами `process.yml`, которые + прошли зелёным Validate. +- Мирроринг `process.yml` в `main` после слияния — по признанию автора и по + тексту issue, это отдельное действие оркестратора/владельца, вне этого + коммита. +- Windows-путь (`spawn-portable.mjs`, `isMainModule`) — прогнан только через + существующий набор `windows-portability.test.mjs` (условные проверки текста + и путей), реальный Windows-раннер не поднимался — так же, как это принято в + проекте для инфраструктурных задач. +- Golden/HA-харнесс/pytest/perf/инварианты модели — не прогонялись: `src/**` + и `custom_components/**/*.py` не затронуты диффом, AC их не называет. +- Полный `npm test` шестью частями и `bundle:budget`/`no-new-any` — не + перегонял отдельно, доверился зелёному Validate на этом же SHA + (https://github.com/Matysh/houseplan-card/actions/runs/35966022704). + +## Вердикт + +Зелёный. AC1–AC3 доказаны исполнением, защитные AC закрыты таблицей +«AC · чем доказан · чем краснеет» с лично прогнанными мутантами, дешёвые гейты +подтверждены зелёным Validate на материале, дополнительные гейты (unit-тесты +правки, 6 мутантов, `process-gate`, `mutation-gate --check`) прогнаны лично и +зелёные. Единственная находка (Low, форматирование абзаца в `PROCESS.md`) +снята без возврата автору. + +--- + + + +## Материал раунда + +- Ветка: `issue/643-rebase-index-conflict`, коммит `92b83d9525fe` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. +- Дерево материала: `466e511b3ef0e4e3bc86926173e4fca2d2d11631` + ``` + git log --all --format='%H %T' | grep 466e511b3ef0 + ``` +- Тело issue: `2e98a17fb8a36d5b607e7a0731bf45eb12384e32f068f62cdc9b04c0b789852d` +- Вердикт конвейера: `green` · High 0 diff --git a/docs/reviews/INDEX.md b/docs/reviews/INDEX.md index 4c8fd170..4feeae0a 100644 --- a/docs/reviews/INDEX.md +++ b/docs/reviews/INDEX.md @@ -1,9 +1,10 @@ # Индекс ревью -Генерируется `node scripts/reviews-index.mjs` (#635) — не редактировать руками. Документов: 1025, issue: 363. Вердикт: 🟢 зелёный · 🟡 жёлтый · 🔴 красный · ⚪ не распознан (свободная форма старых документов). H/M — число High/Medium по строке вердикта или заголовкам находок. Файлы — пути, названные в находках; ищите по имени файла: `grep form-kit INDEX.md`. +Генерируется `node scripts/reviews-index.mjs` (#635) — не редактировать руками. Документов: 1026, issue: 364. Вердикт: 🟢 зелёный · 🟡 жёлтый · 🔴 красный · ⚪ не распознан (свободная форма старых документов). H/M — число High/Medium по строке вердикта или заголовкам находок. Файлы — пути, названные в находках; ищите по имени файла: `grep form-kit INDEX.md`. | Issue | Документ | Этап · раунд | Вердикт | H | M | Находки | Файлы | |---|---|---|---|---:|---:|---|---| +| #643 | [CODE-REVIEW-643-r1.md](CODE-REVIEW-643-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — | | #642 | [SPEC-REVIEW-642-r1.md](SPEC-REVIEW-642-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | — | | #641 | [CODE-REVIEW-641-r1.md](CODE-REVIEW-641-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | нет исполняемого автотеста на ключевой guard в accept.mjs | `accept.mjs` `demo/golden/accept.mjs` `test/golden-wsl-artifact.test.mjs` `wsl-attestation.json` `test/golden-capture-provenance.test.mjs` `scripts/mutation-registry.mjs` `scripts/golden-wsl-artifact.mjs` | | #641 | [CODE-REVIEW-641-r2.md](CODE-REVIEW-641-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | — |