документы

Подзаголовок секции с кратким описанием того, что внутри секции.

CodeGuard PR Review — Quickstart (3–5 минут)

Этот документ объясняет, как быстро читать PR-коммент CodeGuard и какие действия выбрать: принять, запросить правку, упростить, изолировать изменение или углубиться в полный отчёт.

1) Что такое отчёт CodeGuard

CodeGuard — это радар для ревью, который подсвечивает места, где PR нетипично меняет структуру/поведение относительно привычного “рисунка” репозитория.

В отчёте есть три независимых слоя сигнала:

  1. Structure — форма кода (ветвления, вложенность, “разрывы паттерна”, хвосты).
  2. Semantic (effects/taint) — где в диффе появляются side-effects (NET_IO/DB/FILE_IO/EXEC/LOG) и рядом какие источники (USER_INPUT/SECRET/...).
  3. Control — режимы пути исполнения (условия, error-ветки, return-хвосты, init/cleanup).

Важное: это не SAST и не баг-файндер. Инструмент не “доказывает” ошибку — он помогает сфокусировать внимание ревьюера.

2) Два уровня выдачи: PR-коммент vs Full report

Обычно в GitHub вы видите:

2.1. PR-коммент (короткий)

Содержит:

  • общий статус (Status, risk_level, ci_status, analysis full/partial),
  • struct_risk,
  • top-N hotspots (по важности),
  • per-file structural risk,
  • ссылки на Full report: Markdown / JSON.

2.2. Full report (по ссылке)

Нужен, когда:

  • PR большой и хочется “всё”,
  • нужно проверить детали (effects/taint/control/invariants),
  • analysis=partial и важно понять, что именно было пропущено,
  • нужно работать “как с артефактом” (Markdown рядом с diff’ом или JSON для автоматизации).

Top-N hotspots в PR-комменте — это лимит вывода, а не ограничение понимания. Это “первые по приоритету” участки, чтобы не превращать комментарий в простыню.

3) Алгоритм чтения PR-коммента за 3–5 минут (строгий порядок)

Шаг 1. 15 секунд: общий сигнал

Смотрите сразу 4 поля:

  1. risk_level (low|medium|high)
  2. struct_risk (0–100)
  3. ci_status (ok|warn|fail)
  4. analysis (full|partial)

Быстрое правило:

  • risk_level=high → почти всегда идём в hotspots и главный файл.
  • analysis=full → весь код, который CodeGuard умеет анализировать в этом репозитории (на текущем этапе — Python), был обработан.
  • analysis=partial → в PR есть файлы вне профиля анализа (например, YAML/CSV/Markdown и т.п.), и по ним CodeGuard не делал анализ. Python-часть PR при этом обработана полностью.

Шаг 2. 30 секунд: “какой файл главный”

Откройте Per-file structural risk и ответьте:

  • какой файл даёт основную долю риска?
  • риск сосредоточен в одном файле или размазан?

Если один файл тащит всё — ревью ускоряется: начинайте с него.

Шаг 3. 2 минуты: top-3 / top-5 hotspots

Откройте Top hotspots и пройдитесь по первым 3–5 строкам.

Для каждого hotspot ответьте:

  1. Что это за место? (строки, контекст в диффе)
  2. Почему оно может быть нетипичным? (kind)
  3. Насколько срочно? (score — приоритет просмотра)

Если в колонках Effects / Taint / Control что-то есть — начинайте с них. Это обычно более “прикладной” риск, чем чистая форма кода.

Шаг 4. 30 секунд: выбрать действие

После просмотра нескольких hotspots вы должны выбрать один из путей:

  • Accept (и при необходимости пояснить контекст)
  • 🔧 Simplify (упростить участок/снизить “архитектурную массу”)
  • 📦 Isolate (вынести новую подсистему/алгоритм в отдельный модуль/слой)
  • 🧪 Add tests / invariants (если изменение меняет режимы/математику/контракты)
  • 🔎 Open Full report (если сигнал сильный, но контекст непонятен / анализ partial)

4) Как интерпретировать ключевые поля (по делу)

4.1. risk_level и struct_risk

  • struct_risk — число 0–100: “насколько структура выбивается”.
  • risk_level — дискретный уровень для быстрого решения.

Как читать:

  • low → обычно достаточно убедиться, что hotspots не содержат неприятных effects/taint.
  • medium → почти всегда смотрим top-3 hotspots.
  • high → смотрим главный файл + top hotspots и решаем: simplify/isolate/обосновать.

4.2. ci_status

Это сигнал для CI/дашборда:

  • ok → инструмент не видит проблемных пороговых ситуаций.
  • warn → “обязательно посмотри глазами”.
  • fail → в строгом режиме CI это может блокировать (зависит от политики проекта).

4.3. analysis: full|partial

  • full → CodeGuard проанализировал весь поддерживаемый код в PR (на текущем этапе — Python).
  • partial → PR содержит изменения в файлах, которые CodeGuard не анализирует (не-код/неподдерживаемые расширения). В этом режиме отчёт “частичный” не потому, что Python разобран не до конца, а потому что часть PR лежит вне области анализа.

Правило ревью:

  • для Python-части ориентируйся на hotspots и per-file risk;
  • для непрофильных файлов просто ревьюи diff обычным способом (чеклистом проекта), без ожидания сигналов CodeGuard.

4.4. Hotspot: kind и score

  • kind — тип локальной аномалии (что именно “не похоже на норму”).
  • scoreприоритет (какое место смотреть раньше), а не “вероятность бага”.

Типовые kind (интуитивно):

  • struct_pattern_break — “вставили нетипичный структурный рисунок” (часто это новые сущности/паттерны/стиль).
  • depth_spike — “слишком выросла вложенность/условность”.
  • control_outlier — “перегреты пути исполнения” (условия/ошибки/return-хвосты).
  • mixed — смесь факторов.

5) Решение “что делать дальше” (короткая матрица)

Сценарий A: risk_level=low, ci_status=ok, analysis=full

Обычно:

  • быстро пробежать top hotspots (1–3),
  • проверить, нет ли эффектных хвостов/taint,
  • принять PR.

Сценарий B: risk_level=medium или ci_status=warn

Обычно:

  • просмотреть top-3 / top-5 hotspots,
  • если изменения “стилистические/структурные” — предложить упрощение,
  • если изменения алгоритмические — потребовать объяснение + тест.

Сценарий C: risk_level=high

Минимум:

  • открыть главный файл из Per-file structural risk,
  • пройти top hotspots,
  • выбрать: Simplify или Isolate (чаще всего),
  • либо Accept + explain (если изменение осознанно и покрыто).

Сценарий D: analysis=partial

Обычно означает, что PR содержит файлы вне области анализа (например, YAML/CSV/Markdown).

Что делать:

  • Python-часть ревьюим по CodeGuard (per-file risk → top hotspots).
  • Непрофильные файлы (конфиги/данные/доки) ревьюим вручную по стандартам проекта.

6) Что писать в PR-обсуждении (короткие шаблоны)

6.1. Accept + explain (изменение осознанное)

“CodeGuard подсветил нетипичное усложнение в <участок>. Это сделано для <цель>. Добавлены/обновлены тесты <...> и комментарий к контракту.”

6.2. Simplify (снижаем когнитивную нагрузку)

“В этом месте появляется структурный разрыв относительно стиля репозитория. Предлагаю переписать как <простая функция/без lambda/без генератора>, чтобы сохранить предсказуемость кода.”

6.3. Isolate (новая подсистема/паттерн/алгоритм)

“Похоже на внедрение новой мини-архитектуры/алгоритма. Давайте вынесем в отдельный модуль/класс с явным интерфейсом и короткой документацией, чтобы изменение было локализовано.”

6.4. Add tests / invariants (меняется поведение)

“Изменение меняет режим/математику/контракт. Нужны тесты на <инвариант/крайние случаи/стабильность> или явный флаг конфигурации.”

7) Мини-пример (как “прочитать” High risk за 60 секунд)

Если вы видите:

  • Status: High risk
  • struct_risk: 69.49
  • control_risk_level: low
  • hotspots: много struct_pattern_break

Интерпретация:

  • сигнал не про “ветвления/ошибки”, а про внедрение нетипичных структурных паттернов;
  • начните с top-3 hotspots и ответьте: “это новая архитектурная идея (тогда isolate/обосновать) или случайное усложнение (тогда simplify)?”

8) Когда Quickstart недостаточно

Переходите к Full report, если:

  • PR большой, и вам нужно понять картину по файлам целиком,
  • есть Effects/Taint/Control и нужно оценить безопасность/границы/контракты,
  • analysis=partial,
  • изменения выглядят как новая подсистема или новый режим работы.

9) Ссылки на следующие документы

  • REPORT_FIELDS_REFERENCE — справочник всех полей отчёта (подробно).
  • REVIEW_PLAYBOOKS — плейбуки (high struct, high control, effects/taint, partial).
  • CASE_STUDIES — разборы реальных PR (как принимать решения на примерах).

CodeGuard — Report Fields Reference (Summary / Full report / JSON)

Этот документ — справочник по полям отчётов CodeGuard: что означает каждое поле, где оно встречается (PR-коммент / Full report / JSON), как его интерпретировать и какие типовые причины у “странных” значений.

Сначала прочитай: docs/PR_REVIEW_QUICKSTART.md (маршрут чтения за 3–5 минут).

Здесь — именно словарь полей.

0) Контекст: какие “отчёты” бывают

В GitHub PR обычно есть два уровня выдачи:

  1. PR-коммент (Summary)

    Короткий: статусы, top-N hotspots, per-file structural risk, ссылки на Full report.

  2. Full report (Markdown / JSON)

    Полный артефакт: таблицы, дополнительные секции, служебные поля, лимиты анализа.

1) Нотации и договорённости

1.1. Риски и шкалы

  • _risk ∈ [0,100] — нормализованные оценки, удобные для порогов и “светофора”.
  • _risk_level ∈ {low, medium, high} — дискретизация для быстрого решения.
  • Плотности вида _density ∈ [0,1] — доля “сигнальных” токенов/эффектов в выбранной области.

1.2. Локации и строки

  • В Markdown-отчётах локация hotspots обычно вида:path/to/file.py:LINE_START-LINE_END
  • В JSON обычно:
    • file_path
    • line_start / line_end (1-based)
  • В PR-режиме линии трактуются как “строки в новой версии файла” (после применения diff).

1.3. Поля могут быть пустыми

Колонки Effects / Taint / Control в hotspots могут быть пустыми, если:

  • соответствующий канал отключён в конфиге/версии,
  • эвристики ничего не отметили в этом конкретном окне,
  • анализ был partial и кусок не попал в разбор.

2) Summary (PR-коммент): поля и смысл

Типичный заголовок:

  • Status: High risk
  • struct_risk: 69.49
  • hotspots: 10
  • ci_status: warn
  • control_risk_level: low
  • analysis: full|partial

Ниже — расшифровка.

2.1. Status (человеческий статус)

Где: Summary / Full report (Markdown)

Тип: string (обычно Low/Medium/High risk)

Смысл: человекочитаемая “выжимка” по риску/CI-политике.

Практически Status обычно согласован с risk_level + ci_status, но это именно “лейбл”.

2.2. risk_level

Где: Full report (Markdown), часто в JSON (зависит от режима CLI/интеграции)

Тип: enum: low | medium | high

Смысл: дискретный уровень структурного риска (для быстрого решения “копать или нет”).

Типовая интерпретация:

  • low: структурно похоже на типичный код репозитория.
  • medium: стоит глянуть hotspots.
  • high: заметная структурная аномалия — почти всегда ручной просмотр.

2.3. struct_risk

Где: Summary / Full report (Markdown)

Тип: float 0..100

Смысл: глобальный структурный риск для объекта анализа (PR/diff).

Важно:

  • В PR-репорте это “общий” риск по diff.
  • Детализация по файлам — в Per-file structural risk.

В JSON-репортах аналог обычно называется global_struct_risk.

2.4. hotspots (count)

Где: Summary (короткий коммент)

Тип: int

Смысл: сколько hotspots показано в этом комментарии.

Важно:

  • Это не “покрытие” и не “сколько аномалий всего”. Это количество выведенных hotspots (top-N), ограниченное политикой вывода.

2.5. ci_status

Где: Summary / Full report (Markdown), иногда JSON

Тип: enum: ok | warn | fail

Смысл: сигнал для CI/интеграции (как отображать/гейтить).

Типовая политика:

  • ok: пороги не превышены.
  • warn: есть что посмотреть (soft gate).
  • fail: строгий режим (hard gate) — зависит от настройки.

2.6. control_risk_level

Где: Summary / Full report (Markdown), JSON (если включён control-канал)

Тип: enum: low | medium | high

Смысл: дискретный уровень “контрольной сложности” (условия / error-path / return-tail).

Интуиция:

  • low: ветвления/ошибки/return-хвосты выглядят нормально.
  • high: условия и/или error-ветки и/или хвосты перегреты → читать трудно, легко ошибиться.

2.7. analysis: full|partial

Где: Summary / Full report (Markdown)

Тип: enum: full | partial

Смысл: покрытие отчётом поддерживаемых типов файлов.

  • full → весь поддерживаемый код в PR проанализирован (на текущем этапе — Python).
  • partial → в PR есть изменения в файлах, которые CodeGuard не анализирует (например, YAML/CSV/Markdown и т.п.). Python-файлы при этом анализируются полностью; “partial” относится к PR как целому.

Практика:

  • partial не надо трактовать как “Python разобран наполовину”.
  • partial означает: “часть PR лежит вне области анализа и должна ревьюиться вручную”.

3) Per-file structural risk (таблица)

Где: Summary / Full report (Markdown)

Смысл: вклад каждого файла в общий structural-риск.

Колонки:

  • File — путь
  • Risk — структурный риск файла (0..100)

Как читать:

  • “Один файл тащит всё” → начните ревью с него.
  • Риск размазан → вероятно, PR меняет “слой” или стиль сразу в нескольких местах.

В JSON аналог: file_struct_risk: { "path": number, ... }

4) Hotspots: поля строки и как их понимать

Hotspot = локальный участок строк, который выглядит нетипично.

4.1. Поля hotspots в Markdown (таблица)

Типичная строка:

LocationKindScoreEffectsTaintControl

Расшифровка колонок:

Location

  • путь и диапазон строк: file.py:START-END

Kind

Тип: enum

Обычно:

  • struct_pattern_break — “слом типичного структурного рисунка” в окне (нетипичные маркеры IF/LOOP/RETURN/..., новые “паттерны”, смена стиля).
  • depth_spike — локально аномальная вложенность.
  • control_outlier — окно перегрето по control-режимам (условия/error/return).
  • mixed — нет одного доминанта, но окно всё равно выбивается.

Score

Тип: float (ранжирование)

Смысл: приоритет просмотра (чем больше — тем выше в списке).

Важно:

  • scoreне вероятность бага и не абсолютная “оценка качества”.
  • Он нужен, чтобы сортировать hotspots внутри одного отчёта.

Effects

Тип: string / метка / краткая подсказка

Смысл: семантическая подсказка про side-effects (если semantic-канал включён и что-то найдено).

Обычно эффекты (уровень токенов/фрагментов):

  • LOG, DB, FILE_IO, NET_IO, EXEC (и т.п.)

Taint

Тип: string / метка / краткая подсказка

Смысл: источники данных рядом (если включено):

  • USER_INPUT — входные данные
  • SECRET — потенциальные секреты
  • CONST — константы

Control

Тип: string / метка / краткая подсказка

Смысл: контрольный контекст окна:

  • BRANCH_COND, ERROR_PATH, RETURN_PATH, INIT_PATH, CLEANUP_PATH, NORMAL

4.2. Поля hotspots в JSON

Обычно:

  • file_path: string
  • line_start, line_end: int (1-based)
  • segment_id: string (например, hunk_0)
  • segment_kind: enum (diff_hunk, full_file, …)
  • kind: enum
  • score: float
  • (опционально) effect_hint, taint_hint, control_hint

5) Control summary: таблица и поля

Где: Full report (Markdown) и JSON (если включён control-канал)

5.1. Control summary (Markdown)

В PR-репорте может быть таблица:

Fileoverallbranch_conderror_pathreturn_path

Смысл:

  • overall — общий control-риск для файла (обычно агрегат/максимум).
  • branch_cond — риск ветвлений/условий.
  • error_path — риск error-веток.
  • return_path — риск “return/raise-хвостов”.

5.2. Control summary (JSON): типовые поля control_summary.per_file[path]

Полевая модель (часто встречается как “пер-файл агрегаты”):

Доли control-режимов

  • control_share_branch_cond
  • control_share_error_path
  • control_share_return_path
  • control_share_init_path
  • control_share_cleanup_path

Complexity-метрики по режимам

  • control_*_complexity_mean
  • control_*_complexity_max

Cross-метрики “control × effects”

  • branch_cond_effect_density
  • error_path_effect_density
  • return_path_effect_density

Control-риски (0..100)

  • branch_cond_risk
  • error_path_risk
  • return_path_risk
  • overall_control_risk

5.3. Как читать “сильный сигнал” в control-слое

Примеры:

  • branch_cond_risk высокий + branch_cond_effect_density > 0 → в условиях происходит IO/NET/DB/LOG (часто подозрительно и плохо читается).
  • error_path_risk высокий + error_path_effect_density высокий → обработчики ошибок делают много эффектов.
  • return_path_risk высокий + return_path_effect_density высокий → “перед возвратом” происходит эффектная логика (меняет поведение на выходе).

6) Semantic layer: effect_summary, file_semantic_risk, taint

Если semantic-канал включён, в JSON (и иногда в Markdown) появляются поля, связанные с эффектами и “опасными” потоками.

6.1. effect_summary

Где: JSON, иногда Full report (Markdown)

Смысл: агрегаты по эффектам/taint.

effect_summary.per_file[path] (по файлам)

Типовые метрики:

  • effect_density_tail ∈ [0,1]

    Доля “эффектных” токенов (NET/DB/FILE/EXEC/LOG) в хвосте файла/сегмента.

    Интуиция: ближе к 1 → хвост “плотно эффектный”.

  • dangerous_flow_score ∈ [0,1]

    Грубый сигнал, что USER_INPUT проходит рядом с эффектами (NET_IO/DB/EXEC/LOG).

    > 0 → найден хотя бы один потенциально опасный соседний flow.

  • secret_leak_score ∈ [0,1]

    Грубый сигнал, что SECRET встречается рядом с LOG / NET_IO.

    > 0 → возможная утечка секретов.

effect_summary.global

Максимумы/агрегаты по всем файлам отчёта — быстрый ответ “есть ли вообще effect-активность”.

6.2. file_semantic_risk и file_semantic_risk_level

Где: JSON

Тип:

  • file_semantic_risk[path] ∈ [0,100]
  • file_semantic_risk_level[path] ∈ {low, medium, high}

Смысл: repo-aware оценка “семантического” риска файла (эффектный хвост + suspicious flow + поправка по истории).

Частое правило приоритизации:

  • высокий struct_risk и высокий file_semantic_risk по одному и тому же файлу → файл почти всегда стоит ревьюить вручную “в первую очередь”.

6.3. taint (как интерпретировать)

Taint-метки обычно не “про безопасность”, а про источники данных рядом:

  • USER_INPUT → важно смотреть, куда дальше идёт значение (особенно рядом с NET/DB/EXEC/LOG)
  • SECRET → важно смотреть, нет ли рядом логирования/сети

7) Invariants / rule violations (если включены правила)

Если подключены командные правила (семантические инварианты), отчёт может содержать:

  • invariant_violations: список нарушений
    • правило/идентификатор
    • локация
    • краткое объяснение

Это самый “жёсткий” и понятный слой:

правило нарушено → вот место → исправляем или осознанно исключаем.

8) Coverage & scope: что означает “partial analysis”

8.1. Почему отчёт может быть partial

analysis=partial означает: в PR есть файлы вне профиля анализа (на текущем этапе CodeGuard заточен под Python-код). Примеры: YAML, CSV, Markdown, ассеты, данные, конфиги и т.п.

8.2. Как правильно читать partial

  • Для Python-части: отчёт полноценный (per-file risk, hotspots и т.д.).
  • Для непрофильных файлов: CodeGuard молчит, потому что не анализирует их.

8.3. Что делать ревьюеру

  • Ревьюить Python по отчёту (сначала top hotspots).
  • Ревьюить непрофильные файлы вручную: корректность формата, обратная совместимость, миграции, соответствие стандартам проекта, безопасность секретов в конфиге и т.д.

8.4. Что делать продуктово (если нужно)

Если проекту важно, чтобы YAML/Markdown/CSV тоже проходили “автоматический радар”, это отдельный roadmap: подключать анализаторы для новых типов/языков и расширять scope.

9) Метаданные и служебные поля

В зависимости от интеграции/версии могут присутствовать:

  • Generated / timestamp (в Markdown шапке)
  • repo_root, pr_number (в JSON)
  • ссылки Markdown / JSON (в Summary)
  • expires_at_unix_s (в Summary, если ссылки на Full report имеют срок действия) Unix time (секунды), когда токенизированная ссылка/артефакт может истечь.

10) Маппинг имён: Markdown ↔ JSON (часто путают)

  • Markdown struct_risk (в PR Summary/Full report)

    ≈ JSON global_struct_risk

  • Markdown “Per-file structural risk”

    ≈ JSON file_struct_risk

  • Markdown “Hotspots” (таблица top-N)

    ≈ JSON hotspots (может быть шире/полнее в полном артефакте)

  • Markdown analysis: full|partial

    ⇐ выводится из analysis_limits + fallback/ограничений

11) Быстрые ответы на “странные” значения

Почему control_summary везде 0.0?

  • control-канал выключен,
  • или в этом PR control-режимы не выбиваются,
  • или анализ partial и нужные куски не попали.

Почему Effects/Taint пустые в hotspots?

  • semantic-канал выключен,
  • или эвристики не нашли сигналов в данном окне,
  • или отчёт в “reviewer mode” показывает только структурные hotspots.

Почему hotspots “только 10”, но я уверен, что “аномалий больше”?

  • это лимит вывода top-N в PR-комменте; открывайте Full report (Markdown/JSON).

CodeGuard — Review Playbooks (PR)

Этот документ — набор плейбуков “что делать”, когда CodeGuard подсвечивает:

  • высокий структурный риск (high struct),
  • высокий control риск (high control),
  • эффектные хвосты (IO/NET/DB/EXEC/LOG),
  • связку USER_INPUT → (NET/DB/EXEC/LOG),
  • partial analysis из-за лимитов/фолбэков.

Цель плейбуков — не заменить ревью, а быстро выбрать правильный тип реакции:

  • Accept + explain
  • Simplify
  • Isolate
  • Add tests / invariants
  • Split PR / rerun with full analysis

Сначала прочитай маршрут: docs/PR_REVIEW_QUICKSTART.md.

Справочник полей: docs/REPORT_FIELDS_REFERENCE.md.

0) Общие принципы реакции

0.1. Важное: сигналы CodeGuard — не “вердикт”, а “карта внимания”

  • Высокий риск не означает, что код неверный.
  • Низкий риск не означает, что код безопасный или без багов.
  • Смысл отчёта: дать 3–10 мест, куда человеку стоит посмотреть в первую очередь.

0.2. Три базовых действия ревьюера (и когда что выбирать)

  • Simplify — когда сложность возникла “по пути”, без явной архитектурной причины.
  • Isolate — когда изменение похоже на новую подсистему/паттерн/алгоритм.
  • Accept + explain — когда сложность оправдана, но должна быть проговорена и зафиксирована (контракт/тест/комментарий).

0.3. Если есть invariant_violations — это “жёсткий сигнал”

Если включены командные правила (semantics/invariants), нарушения трактуются как:

  • “либо исправить”,
  • либо “осознанно исключить/переопределить правило”, но не игнорировать молча.

1) Быстрый выбор плейбука (30 секунд)

Смотри Summary:

  1. analysis=partialPlaybook: Partial analysis (scope)
  2. risk_level=high и hotspots в основном struct_pattern_break/depth_spikePlaybook: High struct
  3. control_risk_level=high или hotspots control_outlierPlaybook: High control
  4. В hotspots есть Effects/Taint, в JSON виден effect_density_tail/dangerous_flow_score
    • если хвост “плотно эффектный” → Playbook: Effect tails
    • если есть USER_INPUT рядом с NET/DB/EXEC/LOGPlaybook: USER_INPUT → Effects

2) Playbook: High struct (структурная аномалия)

Сигналы

  • risk_level=high и/или высокий struct_risk
  • hotspots в основном:
    • struct_pattern_break
    • depth_spike
    • mixed (если доминирует форма/глубина)
  • control_risk_level может быть low — это нормально: “форма нетипична, но ветвления не перегреты”.

Риск, который снижаем

  • рост когнитивной нагрузки,
  • “скрытая архитектура” внутри файла,
  • появление нетипичных паттернов без контракта (потом это сложно сопровождать).

Быстрый чек (60 секунд)

  1. Какой файл главный в Per-file structural risk?
  2. Top-3 hotspots: что добавилось?
    • новые сущности (класс/подсистема/мини-DSL),
    • генераторы/yield,
    • лямбды/функциональные конструкции,
    • нетипичная математика/алгоритм,
    • резкий рост вложенности.
  3. Ответь: это фича-архитектура или случайное усложнение?

Варианты действий

A) Accept + explain (сложность оправдана)

Подходит, если:

  • изменение действительно приносит ценность (новый алгоритм/режим),
  • есть тесты/контракт,
  • место локализовано, читаемо и объяснено.

Действия:

  • добавить короткий комментарий “почему этот паттерн нужен”,
  • добавить тест(ы) на контракт/крайние случаи,
  • если это “режим” — сделать явный флаг/конфиг и задокументировать.

Шаблон PR-комментария:

“CodeGuard подсветил структурный разрыв в <file:lines>. Изменение осознанное: <зачем>. Добавлены тесты <что проверяем> / комментарий к контракту.”

B) Simplify (снизить архитектурную массу)

Подходит, если:

  • паттерн “красивый”, но не обязателен,
  • можно выразить проще и ближе к стилю репозитория.

Техники упрощения:

  • заменить лямбды на именованные функции,
  • убрать генераторы: сделать простую функцию next_*,
  • уменьшить вложенность (guard clauses, ранние return),
  • разнести “вычисление” и “эффекты” (IO отдельно),
  • сделать шаги алгоритма читаемыми (переменные, имена).

Шаблон PR-комментария:

“Есть структурный разрыв относительно нормы репозитория. Давай перепишем участок более императивно/плоско (без <lambda/yield/…>), чтобы снизить когнитивную нагрузку.”

C) Isolate (новая подсистема/паттерн)

Подходит, если:

  • в файл “приехала” мини-архитектура (strategy/state/manager),
  • появился новый алгоритм, который хочется переиспользовать,
  • правка меняет режим работы и требует явного интерфейса.

Действия:

  • вынести в отдельный модуль/класс,
  • дать явный API,
  • добавить минимальную документацию/README к модулю,
  • добавить тесты на интерфейс, а не на внутренности.

Шаблон PR-комментария:

“Похоже на внедрение новой подсистемы. Давай вынесем в отдельный модуль с явным интерфейсом и тестами — так изменение станет локализованным и понятным.”

3) Playbook: High control (перегретые ветвления / error-path / return-tail)

Сигналы

  • control_risk_level=high (даже если struct_risk средний/низкий)
  • hotspots типа control_outlier
  • В control_summary высокие значения:
    • branch_cond_risk
    • error_path_risk
    • return_path_risk
  • Усилитель сигнала: высокий _effect_density
    • branch_cond_effect_density
    • error_path_effect_density
    • return_path_effect_density

Риск, который снижаем

  • ошибки из-за сложных условий,
  • “программы внутри обработки ошибок”,
  • неожиданные side-effects на выходе (перед return/raise),
  • тестируемость и предсказуемость поведения.

Быстрый чек (60 секунд)

  1. Какая ось перегрета?
    • BRANCH_COND: условия слишком сложные
    • ERROR_PATH: обработка ошибок стала сложной
    • RETURN_PATH: перед return/raise происходит логика/эффекты
  2. Есть ли эффекты внутри этих путей? (если да — это приоритет №1).

Рецепты рефакторинга (по оси)

A) Перегретые условия (BRANCH_COND)

Типовые проблемы:

  • длинные if с вычислениями и IO,
  • цепочки elif как mini-state-machine,
  • сложные условия без имен.

Что делать:

  • вынести условия в именованные функции (is_*, should_*),
  • разорвать на guard clauses,
  • “таблица решений” вместо цепочек elif,
  • перенос эффектов (IO/NET/DB) вне условных выражений.

PR-комментарий:

“Control-слой показывает перегретые условия. Предлагаю вынести условия в именованные предикаты и убрать эффекты из условий — так снизим риск ошибок и улучшим читаемость.”

B) Сложный error-path (ERROR_PATH)

Типовые проблемы:

  • except делает половину бизнес-логики,
  • много эффектов в обработчиках ошибок,
  • смешаны компенсации/логирование/ретраи.

Что делать:

  • минимизировать работу в except: собрать контекст → вызвать handle_error(...),
  • унифицировать обработку ошибок (один путь),
  • явные политики retry/backoff,
  • тесты на ошибочные сценарии (обязательны, если меняется поведение).

PR-комментарий:

“Error-path стал сложным. Давай вынесем обработку ошибок в отдельные функции/модуль, чтобы except не превращался в программу внутри программы.”

C) Тяжёлые return/raise-хвосты (RETURN_PATH)

Типовые проблемы:

  • перед return выполняются эффекты (лог/запись/сеть),
  • хвост меняет контракт функции “на выходе”.

Что делать:

  • разделить: вычислить результат → отдельно выполнить эффекты → вернуть,
  • явно документировать контракт (что гарантируется до return),
  • тесты на контракт “на выходе”.

PR-комментарий:

“В return-path появилась тяжёлая логика/эффекты. Опасно менять контракт на выходе. Предлагаю разделить вычисление результата и эффекты и покрыть контракт тестами.”

4) Playbook: Effect tails (плотный хвост с NET/DB/FILE/EXEC/LOG)

Сигналы

  • В JSON/Full report высокий effect_density_tail (по файлу или по PR)
  • TL;DR/подсказки уровня “Хвост с NET/DB/FILE-эффектами…”
  • В hotspots есть Effects (например NET_IO+DB+LOG) без явных Taint

Риск, который снижаем

  • сложная, плохо тестируемая “эффектная” концовка функций/модулей,
  • скрытая зависимость от порядка операций,
  • неявная транзакционность (частичный успех),
  • нестабильность на ошибках (частичное состояние).

Быстрый чек (2 минуты)

  1. Что это за эффекты в хвосте?
    • DB / NET_IO / EXEC — высокий приоритет
    • LOG — зависит от контекста, но при большом объёме может быть “шумом”
  2. Есть ли явная граница “pure → effects”?
  3. Что происходит при ошибке в середине хвоста?
  4. Есть ли идемпотентность / транзакционность / компенсации?

Рекомендуемые действия

  • Вынести эффекты в отдельный слой/функцию (persist_*, send_*, exec_*)
  • Сделать порядок операций явным (и задокументировать)
  • Добавить тесты:
    • на частичные ошибки,
    • на повторный запуск (идемпотентность),
    • на таймауты/ретраи (если сеть).

PR-комментарий:

“В хвосте файла/функции концентрируются IO/NET/DB эффекты. Давай выделим ‘эффектный слой’ и добавим тесты на ошибки/идемпотентность, чтобы поведение было предсказуемым.”

5) Playbook: USER_INPUT → (NET/DB/EXEC/LOG) (подозрительные потоки)

Сигналы

  • dangerous_flow_score > 0
  • hotspot имеет taint_hint=USER_INPUT и effect_hint включает NET_IO/DB/EXEC/LOG
  • TL;DR/подсказки “USER_INPUT рядом с NET/DB/LOG…”

Риск, который снижаем

Это не полноценный dataflow-анализ, но практический риск понятен:

  • входные данные могут попадать в сеть/БД/exec/log без проверок,
  • риск инъекций/утечек/лог-спама/неожиданных side-effects.

Быстрый чек (2–3 минуты)

  1. Где граница доверия?
    • валидация, нормализация, ограничения, escaping
  2. Есть ли allowlist (параметры/команды/поля)?
  3. Логируются ли входные данные? Если да — нет ли PII/секретов?
  4. Для DB/NET/EXEC: есть ли явный слой, который отвечает за безопасность/контракты?

Рекомендуемые действия (по типу эффекта)

A) USER_INPUT → DB

  • параметризованные запросы/ORM
  • строгая схема валидации
  • ограничения длины/формата
  • тесты на “плохие” входы

B) USER_INPUT → NET_IO

  • allowlist доменов/эндпоинтов
  • timeouts/retries policy
  • ограничение размеров payload
  • логирование без утечек

C) USER_INPUT → EXEC

  • запрет или строгий allowlist
  • никакой сборки shell-команд строками
  • отдельный слой “исполнитель”, покрытый тестами

D) USER_INPUT → LOG

  • маскирование/редакция
  • ограничение объёма
  • исключение секретов/PII

PR-комментарий:

“Есть участок, где USER_INPUT находится рядом с NET/DB/EXEC/LOG. Давай явно зафиксируем границы: валидацию/allowlist/маскирование и добавим тесты на небезопасные входы.”

6) Playbook: Partial analysis (scope: PR содержит непрофильные файлы)

Сигналы

  • analysis=partial

Что это означает

PR содержит файлы вне области анализа CodeGuard (на текущем этапе — не Python: YAML/CSV/Markdown и т.п.). Для Python-части отчёт остаётся полноценным.

Риск, который снижаем

  • пропустить важные изменения в конфигурации/данных/документации, потому что “инструмент ничего не сказал”.

Что делать ревьюеру (быстрый сценарий)

  1. Python-изменения ревьюим по CodeGuard: per-file risk → top hotspots → решение.
  2. Непрофильные файлы ревьюим вручную:
    • конфиги: валидность схемы, backward compatibility, секреты, окружения
    • данные/CSV: формат, миграции, размер/дифф-шум
    • Markdown/доки: корректность инструкций/примеров/команд

Комментарий в PR (корректная формулировка)

“analysis=partial — в PR есть непрофильные файлы (конфиги/данные/доки), CodeGuard анализирует только Python-часть. По этим файлам делаем обычное ручное ревью.”

7) Мини-шаблоны “что попросить в PR” (коротко, но по делу)

7.1. Когда просить “обоснование”

“Это осознанная архитектурная идея или случайное усложнение? Если осознанная — добавь краткое rationale и тест на контракт.”

7.2. Когда просить “локализацию”

“Похоже на новую подсистему. Давай вынесем в отдельный модуль/класс с явным API.”

7.3. Когда просить “тесты”

“Изменение меняет режим/контракт/эффектный хвост. Нужны тесты на ошибки/крайние случаи/идемпотентность.”

7.4. Когда просить “split PR”

“Сейчас analysis partial. Разбей PR или уменьшай hunks, чтобы отчёт был полным.”

8) Полезная привычка команды

Если CodeGuard регулярно подсвечивает одни и те же классы проблем, команда может:

  • формализовать правила в .codeguard/semantics.yaml (invariants),
  • закрепить стиль “эффекты отдельно от вычислений”,
  • договориться: “если внедряем новый паттерн — isolate + контракт + тест”.

Это превращает “одноразовый сигнал” в системное улучшение архитектуры.

Case study: PR1 (PythonRobotics) — как читать struct_pattern_break на реальном PR

Цель кейса: показать на конкретном PR, что именно CodeGuard подсветил, как это выглядело в отчёте, какой кусок кода за этим стоит и почему это важно при ревью.

1) Что показал отчёт (как это видел ревьюер)

Repo/PR: */PythonRobotics_test PR #1

Status: High risk

struct_risk: 69.49

control_risk_level: low

analysis: full

Per-file structural risk

  • ArmNavigation/n_joint_arm_to_point_control/n_joint_arm_to_point_control.py — 69.49
  • ArmNavigation/__init__.py — 0.00

Top hotspots (10) Все 10 — struct_pattern_break (разрыв типичного структурного паттерна в окне строк).

2) Что мы действительно меняли (и что подсветилось)

Ниже — перечень нетипичных изменений, внесённых в код специально для теста:

2.1. Структурные/архитектурные (нетипичные)

  • Enum вместо констант ✅ (не подсвечено)
  • Dataclass KinematicsConfig ✅ (не подсвечено)
  • Класс InverseKinematicsSolver ✅ (не подсвечено)
  • Приватный класс _StateManager 🟡 (подсвечено)
  • Паттерн “Стратегия” через лямбды 🟡 (подсвечено)
  • Callback-функции 🟡 (подсвечено)
  • Генератор с yield и сложной логикой 🟡 (подсвечено)

2.2. Алгоритмические (нетипичные)

  • Демпфирование Якобиана 🟡 (подсвечено)
  • Нелинейное масштабирование 🟡 (подсвечено)
  • Нормальное распределение в get_random_goal 🟡 (подсвечено)
  • Адаптивный коэффициент Kp ✅ (не подсвечено)
  • Кэширование тригонометрии ✅ (не подсвечено)

2.3. Стилистические (нетипичные)

  • Лямбда accumulator в цикле 🟡 (подсвечено)
  • Сложное условие в генераторе 🟡 (подсвечено)
  • Последовательные трансформации 🟡 (подсвечено)

2.4. Базовые улучшения (нормальные)

  • typing-импорты ✅ (не подсвечено)
  • error handling ✅ (не подсвечено)
  • улучшение именования ✅ (не подсвечено)
  • комментарии/docstrings ✅ (не подсвечено)

3) Разбор подсвеченных hotspots: “как это выглядит” → “что в коде” → “почему важно”

Формат ниже — ровно то, что нужно ревьюеру:

  1. строка из отчёта,
  2. какой паттерн в коде,
  3. почему это важно,
  4. что делать.

Примечание: control_risk_level=low означает, что сигнал здесь не про перегретые ветвления/ошибки, а именно про структурный дрейф.

Hotspot 1 — _StateManager (приватный класс)

Report: ...py:286-286 — struct_pattern_break (score=1.7802)

Что в коде (пример):

class _StateManager:
    def __init__(self):
        self.current = ArmState.WAIT_FOR_NEW_GOAL

    def validate_transition(self, new_state: ArmState) -> bool:
        ...

Почему важно: в файл приходит “мини-подсистема управления состоянием” (новая архитектурная сущность), которую нужно либо осознанно принять, либо локализовать, иначе она разрастётся “по месту”.

Что делать (варианты):

  • Isolate: вынести менеджер состояния в отдельный модуль/класс с явным API + тестами переходов.
  • Simplify: если не нужен — вернуться к простым константам/таблице переходов.

Hotspot 2 — генератор целей с yield

Report: ...py:175-175 — struct_pattern_break (score=1.7209)

Что в коде (пример):

def _goal_generator():
    while True:
        goal = get_random_goal(...)
        yield goal

Почему важно: генератор меняет модель исполнения (появляется поток значений и “скрытое состояние”), что влияет на читаемость и дебаг.

Что делать:

  • Simplify: заменить на явную next_goal() без yield.
  • Accept + explain: если генератор нужен — короткое rationale + тесты поведения.

Hotspot 3 — переключение распределения (gaussian vs uniform)

Report: ...py:162-164 — struct_pattern_break (score=1.6677)

Что в коде (пример):

if use_gaussian:
    return np.array([random.gauss(mu, sigma), ...])
return np.array([random.uniform(a, b), ...])

Почему важно: это меняет режим/динамику поведения (распределение целей) — алгоритмическая смена, которую ревьюер должен увидеть сразу.

Что делать:

  • Accept + explain: зачем два режима, как выбирается, и чем отличается поведение.
  • Add tests: проверка ожидаемого поведения в обоих режимах (хотя бы на уровне инвариантов/ограничений).

Hotspot 4 — лямбда accumulator внутри вычисления

Report: ...py:307-307 — struct_pattern_break (score=1.5986)

Что в коде (пример):

accumulator = lambda x, y, l, a: x + l*np.cos(a) + y*np.sin(a)
...
for ...
    v = accumulator(...)

Почему важно: нетипичная форма записи (функциональный стиль “внутри цикла”) ухудшает прозрачность вычисления.

Что делать:

  • Simplify: заменить на именованную функцию или инлайн-формулу с говорящими переменными.

Hotspot 5 — демпфирование Якобиана

Report: ...py:248-249 — struct_pattern_break (score=1.5452)

Что в коде (пример):

JTJ = J.T @ J
damped = JTJ + damping * np.eye(N_LINKS)
return np.linalg.inv(damped) @ J.T

Почему важно: это изменение численного метода (стабильность/сходимость/поведение решения), которое должно быть осознанным и проверенным.

Что делать:

  • Accept + explain: почему damping нужен, какие значения, какой эффект ожидаем.
  • Add tests: тест на сходимость/устойчивость на типовых сценариях.

Hotspot 6 — нелинейное масштабирование через tanh

Report: ...py:321-323 — struct_pattern_break (score=1.5452)

Что в коде (пример):

distance = np.linalg.norm(target - current)
scale = np.tanh(distance * 2) / 2.0 + 0.5
delta *= scale

Почему важно: нелинейность меняет динамику управления/обновления — это влияет на поведение системы, даже если код “выглядит аккуратно”.

Что делать:

  • Accept + explain: зачем нелинейность, что она улучшает.
  • Add tests/validation: хотя бы проверка монотонности/ограничений/стабильности.

Hotspot 7 — “стратегии восстановления” через лямбды + np.roll

Report: ...py:335-335 — struct_pattern_break (score=1.5452)

Что в коде (пример):

recovery_strategies = [
    lambda x: -x,
    lambda x: np.clip(x, -1, 1),
    lambda x: np.roll(x, 1),
]

Почему важно: это внедрение паттерна “Стратегия” (композиция трансформаций), т.е. фактически новая архитектурная идея.

Что делать:

  • Isolate: вынести стратегию восстановления в отдельный компонент с явным контрактом.
  • Accept + explain: описать порядок стратегий и что считается “успехом восстановления”.

Hotspot 8 — сложное условие на историю целей (all(...) for ...)

Report: ...py:176-177 — struct_pattern_break (score=1.5399)

Что в коде (пример):

if all(np.linalg.norm(goal - g) > 2.0 for g in goal_history[-3:]):
    yield goal

Почему важно: сложная логика фильтрации встроена внутрь генератора — это усложняет понимание правил выбора цели.

Что делать:

  • Simplify: вынести в предикат is_goal_far_enough(goal, history) + тесты на крайние случаи.

Hotspot 9 — callback-замыкание

Report: ...py:146-146 — struct_pattern_break (score=1.5228)

Что в коде (пример):

def log_callback(iter_idx, err, angles):
    logger.info("...")

solver.solve(..., callback=log_callback)

Почему важно: появляется новый “hook” расширяемости; команда должна понимать, это принятный паттерн для репозитория или нет.

Что делать:

  • Accept (если это норма): оставить и стандартизировать интерфейс callback’а.
  • Simplify (если не норма): заменить на явный лог/телеметрию в одном месте.

Hotspot 10 — последовательные трансформации (стратегии в цикле)

Report: ...py:259-259 — struct_pattern_break (score=1.4533)

Что в коде (пример):

for strategy in recovery_strategies:
    delta = strategy(delta)

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

Что делать:

  • Isolate + контракт: “recovery engine” как отдельная функция/модуль.
  • Add tests: что считается корректным восстановлением и когда цикл заканчивается.

4) Почему этот кейс полезен для команды

  1. Он показывает, как читать struct_pattern_break на реальном коде: это не “стиль отступов”, а места, где PR приносит новые сущности/паттерны/алгоритмическую смену.
  2. Он даёт ревьюеру практический формат реакции: Simplify / Isolate / Accept+explain / Add tests — прямо по подсвеченным строкам.
  3. Он демонстрирует, что CodeGuard в этом прогоне фокусируется на нетипичных паттернах, не превращая обычные улучшения (typing/docstrings/имена/error handling) в шум.

©SynqraTech, 2025

Build systems that reconstruct the structure of reality