fix(session-marks): в TS/JS сканируются комментарии, а не строка целиком #21

Merged
claude-secretary merged 2 commits from fix/session-marks-comment-scan into main 2026-08-27 12:29:20 +07:00

Закрывает остаток #4. Первая половина задачи (no-issue-refs-in-comments на фронте, общий слой разбора комментариев) уже приехала в #10 и #12; это вторая.

Проблема

no-session-marks-in-code в .py берёт COMMENT/STRING-токены, а в любом не-.py файле шёл построчно по всей строке. Для markdown это верно — там проза и есть предмет проверки. Для .ts/.tsx — нет: правило [A-Z]\d+ намеренно широкое, и под него попадает обычный код:

export function identity<T1>(value: T1): T1 { ... }   // T1
export enum Step2 { First = "H2" }                    // Step2, H2

Пока хук на фронт не наводили, дыры не было видно. Но и покрыть им фронт было нельзя — ровно то, ради чего задача заводилась (senokosov/baton#688: золотые модули фронта грепали на метки руками).

Что сделано

C-подобные расширения разбираются c_style_chunks из scripts/comment_scan.py — той же машиной состояний, что у no-issue-refs-in-comments: переживает /* */, несколько комментариев на одной строке и строковые литералы, внутри которых // не открывает комментарий. Логика разбора остаётся одна на все хуки, а не третьей копией.

Разбивка по языкам теперь такая:

Файл Что сканируется
.py, .pyi COMMENT/STRING-токены (tokenize) — деление a / b меткой не считается
.ts, .tsx, .js, … только комментарии // и /* */
markdown и остальное построчно

Метка внутри строкового литерала TS не ловится, хотя в .py такая ловится. Асимметрия намеренная и записана в README: в TS/JS строковый литерал сплошь и рядом несёт разметку ("H2", "P1"), и скан литералов дал бы ложняки на пустом месте.

Заодно чтение в utf-8-sig и UnicodeDecodeError в except: бинарник, попавший под files: потребителя, больше не роняет прогон стектрейсом вместо вердикта. Та же дыра, что закрывалась в #12 и #13 у соседних хуков.

Проверка

  • Фикстуры fixtures/session_marks/good.ts (дженерик, enum, литерал разметки, деление — всё, что построчный скан принял бы за метку), bad.ts (строчный и блочный комментарии), bom.ts.
  • Кейсы в fixtures/run_smoke.sh, номера строк пиннятся через run_case_out — блочный комментарий проверяется отдельной строкой вердикта.
  • Мутации, каждая роняет smoke: вернуть TS на построчный скан (краснеет good.ts), убрать UnicodeDecodeError из except.
  • BOM-кейс честно помечен как фиксирующий поведение, а не ловящий мутацию: в TS BOM безвреден (в отличие от .py, где ронял бы tokenize).

Версия

0.16.00.17.0: поведенческое изменение. Потребителям после бампа стоит ждать других срабатываний на фронте — прежние ложняки на коде исчезнут, а метки в комментариях начнут ловиться.

Пин в examples/ подсказывает, что фронт теперь можно скоупить: files: '^(src|tests)/.*\.(py|ts|tsx)$|\.md$'.

Порядок в очереди: этот PR идёт после #20 (0.16.0); если порядок мержа поменяется, номер поправлю.

Закрывает остаток [#4](https://git.homedevlab.ru/senokosov/pre-commit-hooks/issues/4). Первая половина задачи (`no-issue-refs-in-comments` на фронте, общий слой разбора комментариев) уже приехала в [#10](https://git.homedevlab.ru/senokosov/pre-commit-hooks/pulls/10) и [#12](https://git.homedevlab.ru/senokosov/pre-commit-hooks/pulls/12); это вторая. ## Проблема `no-session-marks-in-code` в `.py` берёт COMMENT/STRING-токены, а в **любом** не-`.py` файле шёл построчно по всей строке. Для markdown это верно — там проза и есть предмет проверки. Для `.ts/.tsx` — нет: правило `[A-Z]\d+` намеренно широкое, и под него попадает обычный код: ```ts export function identity<T1>(value: T1): T1 { ... } // T1 export enum Step2 { First = "H2" } // Step2, H2 ``` Пока хук на фронт не наводили, дыры не было видно. Но и покрыть им фронт было нельзя — ровно то, ради чего задача заводилась (`senokosov/baton#688`: золотые модули фронта грепали на метки руками). ## Что сделано C-подобные расширения разбираются `c_style_chunks` из `scripts/comment_scan.py` — той же машиной состояний, что у `no-issue-refs-in-comments`: переживает `/* */`, несколько комментариев на одной строке и строковые литералы, внутри которых `//` не открывает комментарий. Логика разбора остаётся одна на все хуки, а не третьей копией. Разбивка по языкам теперь такая: | Файл | Что сканируется | |---|---| | `.py`, `.pyi` | COMMENT/STRING-токены (`tokenize`) — деление `a / b` меткой не считается | | `.ts`, `.tsx`, `.js`, … | только комментарии `//` и `/* */` | | markdown и остальное | построчно | **Метка внутри строкового литерала TS не ловится**, хотя в `.py` такая ловится. Асимметрия намеренная и записана в README: в TS/JS строковый литерал сплошь и рядом несёт разметку (`"H2"`, `"P1"`), и скан литералов дал бы ложняки на пустом месте. Заодно чтение в `utf-8-sig` и `UnicodeDecodeError` в `except`: бинарник, попавший под `files:` потребителя, больше не роняет прогон стектрейсом вместо вердикта. Та же дыра, что закрывалась в [#12](https://git.homedevlab.ru/senokosov/pre-commit-hooks/pulls/12) и [#13](https://git.homedevlab.ru/senokosov/pre-commit-hooks/pulls/13) у соседних хуков. ## Проверка - Фикстуры `fixtures/session_marks/good.ts` (дженерик, enum, литерал разметки, деление — всё, что построчный скан принял бы за метку), `bad.ts` (строчный и блочный комментарии), `bom.ts`. - Кейсы в `fixtures/run_smoke.sh`, номера строк пиннятся через `run_case_out` — блочный комментарий проверяется отдельной строкой вердикта. - Мутации, каждая роняет smoke: вернуть TS на построчный скан (краснеет `good.ts`), убрать `UnicodeDecodeError` из `except`. - BOM-кейс честно помечен как фиксирующий поведение, а не ловящий мутацию: в TS BOM безвреден (в отличие от `.py`, где ронял бы `tokenize`). ## Версия `0.16.0` → **`0.17.0`**: поведенческое изменение. Потребителям после бампа стоит ждать других срабатываний на фронте — прежние ложняки на коде исчезнут, а метки в комментариях начнут ловиться. Пин в `examples/` подсказывает, что фронт теперь можно скоупить: `files: '^(src|tests)/.*\.(py|ts|tsx)$|\.md$'`. Порядок в очереди: этот PR идёт после [#20](https://git.homedevlab.ru/senokosov/pre-commit-hooks/pulls/20) (`0.16.0`); если порядок мержа поменяется, номер поправлю.
fix(session-marks): в TS/JS сканируются комментарии, а не строка целиком
All checks were successful
ci / smoke (push) Successful in 15s
ci / smoke (pull_request) Successful in 15s
933c9b1f2b
Остаток hooks#4. В .py хук берёт COMMENT/STRING-токены, а в любом
не-.py файле шёл построчно по всей строке. Для markdown это верно
(проза), для .ts/.tsx — нет: правило `[A-Z]\d+` намеренно широкое, и
дженерик `T1`, `enum Step2`, литерал "H2" под него попадают, меткой не
будучи. Пока хук на фронт не наводили, дыры не было видно — но и
покрыть фронт им было нельзя, ради чего задача и заводилась.

Теперь C-подобные расширения разбираются `c_style_chunks` из
`comment_scan` — той же машиной состояний, что у
no-issue-refs-in-comments: переживает `/* */`, несколько комментариев
на строке и строковые литералы, в которых `//` не открывает
комментарий.

Метка внутри строкового литерала TS при этом не ловится, хотя в .py
ловится. Асимметрия намеренная и записана в README: в TS/JS литерал
сплошь и рядом несёт разметку ("H2", "P1").

Заодно чтение в utf-8-sig и UnicodeDecodeError в except: бинарник в
скоупе больше не роняет прогон стектрейсом.

Мутации, каждая роняет smoke: вернуть TS на построчный скан (краснеет
good.ts), убрать UnicodeDecodeError из except.

Версия 0.16.0 → 0.17.0: поведенческое изменение, консьюмерам после
бампа стоит ждать других срабатываний на фронте.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
fix(session-marks): ниты ревью — ложный пример, .mts/.cts, границы режима
All checks were successful
ci / smoke (push) Successful in 15s
ci / smoke (pull_request) Successful in 15s
06cb8cb4f8
- `enum Step2` правилом НЕ матчится (перед цифрой стоит `p`), а пример
  был разнесён по манифесту, README, докстрингу и smoke. Заменён на
  реально матчащиеся `T1` и `Map<K1, V2>`; фикстура good.ts тоже —
  иначе кейс не про то, из-за чего краснело.
- Кейсы .proto и .mts теперь различают режимы: в good-файлах есть код,
  который построчный скан зафлагал бы (`H2`, `Map<K1, V2>`). До этого
  усечение SLASH_SUFFIXES smoke переживало.
- `.mts`/`.cts` добавлены в SLASH_SUFFIXES: легальные TS-расширения, для
  которых иначе оставался ровно тот построчный скан, от которого уходим.
- README: снят абзац, противоречивший новому описанию (он всё ещё
  утверждал «в прочих файлах — построчно по всему тексту»); пример
  consumer-конфига расширен на ts/tsx — это пункт приёмки hooks#4;
  дописаны границы режима — текст JSX не сканируется, апостроф в JSX
  глушит комментарий своей строки, а `.css`/`.vue`/`.sql` падают в
  построчный фолбэк, где код флагается (ведётся в hooks#11).
- Номера строк: `split("\n")` вместо `splitlines()` — последний режет
  ещё и по \f и U+2028, а comment_scan считает строки только по \n, и
  улика уезжала на чужую строку.
- `is_python(path)` вместо регистрозависимого endswith: `A.PY` уходил в
  построчный скан.
- Кейс на не-UTF-8 гоняется на чужой фикстуре, поэтому проверяется её
  существование: несуществующий путь дал бы OSError → exit 0 → зелёный.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
claude-secretary force-pushed fix/session-marks-comment-scan from 06cb8cb4f8
All checks were successful
ci / smoke (push) Successful in 15s
ci / smoke (pull_request) Successful in 15s
to 1023348870
All checks were successful
ci / smoke (push) Successful in 16s
ci / smoke (pull_request) Successful in 16s
2026-08-27 12:28:56 +07:00
Compare
claude-secretary deleted branch fix/session-marks-comment-scan 2026-08-27 12:29:20 +07:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
senokosov/pre-commit-hooks!21
No description provided.