Перейти к основному содержимому

Контроль архитектуры в CodeGraph

Как описать архитектурную модель, проверить правила на CPG и постепенно включить release gate. См. примеры и проверки перед применением.

Руководства

Архитектурный контроль нужен, когда в проекте есть договорённости о зависимостях: например, интерфейс не должен обращаться к хранилищу напрямую, а доменная логика не должна импортировать HTTP-обработчики. Обычные тесты поведения могут оставаться зелёными после такого изменения. Здесь проверяется само направление связей между частями программы.

Руководство предназначено для разработчика, который описывает границы модулей, и инженера CI, который подключает проверку к выпуску. Для первого шага нужны знакомство с YAML и доступ к GoCPG. Знание внутренних Go-тестов не требуется.

Пример и ожидаемый результат

Рассмотрим Python-проект с двумя каталогами: src/ui/ содержит код интерфейса, src/storage/ — доступ к данным. Мы хотим запретить прямой импорт из UI в Storage. Для реального приложения между ними обычно выделяют сервисный слой; в первом правиле достаточно описать две проверяемые границы.

Компонент — группа файлов или символов. Модель задаёт эти группы, правило задаёт разрешённые отношения между ними. CPG хранит обнаруженные в коде связи. Нарушение (finding) должно указывать на конкретную связь и место в исходнике. Решение о выпуске (release gate) дополнительно зависит от полноты анализа, принятого долга и утверждённых исключений.

Подготовка правила и проверка исходников — разные этапы. Ниже можно воспроизвести первый: проверить YAML, применимость правила к Python и описание компонента. architecture validate не читает исходники и не строит CPG. Даже результат applicable: true не означает, что в проекте нет нарушений.

Подготовка окружения

Используйте сборку GoCPG, в которой есть gocpg architecture --help. Если команда отсутствует, установленный бинарный файл не поддерживает этот раздел. В дереве исходников текущую версию можно собрать из каталога gocpg:

go build -o ./bin/gocpg ./cmd/gocpg

Версия Go указана в gocpg/go.mod. Добавьте каталог бинарного файла в PATH или заменяйте gocpg полным путём к нему. Проверка YAML не требует локального сервера CodeGraph или базы проекта. Чтение сохранённых findings через explain требует сборку с CGO/DuckDB.

Следующие команды запускаются из корня checkout. Файл gocpg/docs/examples/architecture-control/architecture.yaml входит в исходники. Его полное содержимое приведено ниже; пример не требует дописывать скрытые поля.

Модель и первое правило

У правила стабильный идентификатор example.ui-no-storage. У компонентов отдельные uid: сохраняйте их при переименовании читаемого id, чтобы сравнение результатов не воспринимало компонент как новый.

Селектор paths выбирает файлы относительно корня анализируемого проекта. components выбирает уже объявленные компоненты по идентификаторам. import — тип ребра, а imports — название требуемой возможности анализатора; это разные поля с разными значениями.

spec_version: "2.0"
kind: architecture
id: example.ui-no-storage
version: "1"
fingerprint_version: 1
languages: [python]
architecture:
  source_roots:
    - path: src
      source_set: production
  classification:
    exhaustive: true
    conflict_policy: error
    unclassified_policy: error
    generated_policy: ignore
    minimum_coverage: 1.0
  components:
    - uid: cmp-example-ui
      id: ui
      name: UI
      kind: leaf
      match:
        paths: ["src/ui/**"]
    - uid: cmp-example-storage
      id: storage
      name: Storage
      kind: leaf
      match:
        paths: ["src/storage/**"]
  contract: forbidden_dependency
  from:
    components: [cmp-example-ui]
  to:
    components: [cmp-example-storage]
  relation:
    edge_types: [import]
    reachability: direct
  languages: [python]
  required_capabilities: [imports, source_sets]
  minimum_coverage: 1.0

source_roots определяет производственный код. Требование полной и однозначной классификации означает: файл в этом scope должен попасть в компонент; конфликт селекторов и неизвестная доля покрытия должны быть разобраны до enforcement. В этом небольшом примере других производственных каталогов нет.

forbidden_dependency запрещает связи от from к to. reachability: direct проверяет непосредственный импорт. Если задача — запретить достижимость через промежуточные компоненты, потребуется транзитивное правило; менять этот смысл незаметно для существующего baseline нельзя.

Проверка применимости

gocpg architecture validate --file gocpg/docs/examples/architecture-control/architecture.yaml --language python --json

В JSON проверьте следующие поля; остальные поля ответа здесь опущены:

{
  "rule_id": "example.ui-no-storage",
  "applicable": true,
  "gate_evaluated": false,
  "reason_codes": ["applicable"]
}

applicable подтверждает совместимость правила с объявленными возможностями языка. Поле gate_evaluated: false сообщает, что решения о выпуске ещё нет. Для неподходящего языка, например --language c, команда может завершиться с кодом 0, но вернуть applicable: false и language_not_selected. Поэтому одной проверки exit code в CI недостаточно: нужно читать JSON.

Синтаксическая ошибка, неизвестное поле YAML, отсутствующий файл или неверный CLI-флаг приводят к ненулевому коду завершения. Для этой группы команд нужны --file и --json; флаги --rule и --format json не поддерживаются.

Проверка описания компонента

gocpg architecture describe --file gocpg/docs/examples/architecture-control/architecture.yaml --component ui --json

В ответе ожидаются rule_id: example.ui-no-storage, component.uid: cmp-example-ui и component.id: ui. Если запрошенный компонент отсутствует, команда завершится ошибкой. Проверьте, что прочитана нужная модель, прежде чем искать нарушение в графе.

describe возвращает объявление компонента, его родителя и непосредственных детей. Оно не показывает фактическую классификацию файлов: исходники этой командой не читаются.

От правила к проверке проекта

Следующий этап требует построенного CPG и запуска архитектурного evaluator. В группе architecture сейчас зарегистрированы validate, describe и explain; универсальной команды запуска произвольного правила в этой группе нет. Команда scan обслуживает отдельный контур pattern rules. Применимость YAML сама по себе не подтверждает доступность полного сценария оценки этого правила через CLI.

При использовании настроенной интеграции CodeGraph результат должен содержать ревизию исходников, идентификатор запуска, выполненные правила и полноту анализа. Для разбора конкретного finding возьмите run_id из этого результата, а fingerprint из самого finding. Без сохранённого запуска команда explain не сможет восстановить объяснение. Ниже описаны условия внедрения release gate; перед его включением нужен проверенный путь выполнения анализа в вашей интеграции.

Как внедрять без ложного зелёного статуса

  1. Зафиксируйте компоненты и классификацию; добейтесь понятного списка unclassified и conflicts.
  2. Закрепите точные версии language preset и rule set. Алиас latest для enforcement не подходит.
  3. Запустите профиль discovery. Его результат служит для настройки модели и не имеет release authority.
  4. Перейдите в advisory, разберите false positives и сформируйте baseline только для осознанно принятого долга.
  5. Оформляйте временное исключение как waiver с владельцем, согласующим, причиной, ссылкой на решение и сроком действия.
  6. Включите enforce_new, когда CPG свежий, все активные правила выполнены, а classification, resolution и capability coverage подтверждены.
  7. Используйте enforce_all или strict только после устранения либо явного governance для известного долга.

Статусы discovery и advisory дают NOT_APPLICABLE, а не разрешение на выпуск. В enforcing-профилях только PASS означает release_allowed=true. Нарушения дают FAIL_VIOLATIONS; устаревший CPG, неполный запуск, неизвестный знаменатель coverage, отсутствующая capability или непроверенный manifest дают BLOCKED_INCOMPLETE.

Baseline, waiver и доверие

Baseline хранит известный finding для точной версии fingerprint и semantic hash правила. Изменение смысла правила переводит решение в needs_review; локальный файл сам по себе ничего не подавляет.

Waiver относится к точному finding и scope. Он не переносится автоматически на переименованный символ или новый fingerprint. Просроченные, отозванные и неиспользуемые исключения остаются наблюдаемыми findings.

Доверенный governance manifest привязан к проекту, репозиторию, commit, hashes модели и правил, сроку действия и монотонной ревизии. Ключи доверия и сведения об отзыве поступают из control plane или CI, а не из проверяемого репозитория.

Полный и инкрементальный запуск

incremental_complete допустим только при том же конечном наборе fingerprints, baseline/waiver states и semantic digest, что и у полного запуска. Изменение модели, public API, baseline, waiver, dependency edge или strongly connected component расширяет invalidation. При неполном результате enforcement выполняет одну синхронную полную попытку; любой неполный итог остаётся BLOCKED_INCOMPLETE.

Обновление CPG сначала публикует подготовленную файловую delta, затем выполняет ограниченный semantic reconciliation по authoritative source manifest. Граф получает статус clean только после проверки stale generations, dangling edges, индексов, state и охваченных производных строк. Physical compaction запускается отдельно. Подробный контракт приведён в разделе разделе об инкрементальном сканировании.

Диагностика результата

gocpg architecture describe --file gocpg/docs/examples/architecture-control/architecture.yaml --component ui --json
gocpg architecture explain --db ./cpg.duckdb --run-id RUN --fingerprint SHA256 --json

describe показывает компонент с родителем и непосредственными детьми. explain читает сохранённый канонический finding, locations и code flow. Эти команды доступны только для чтения и не создают отдельный источник истины.

Единое руководство и справочные приложения

Эти 17 тем образуют один рабочий цикл. Читателю не нужно открывать их в произвольном порядке: сначала выполняется сквозной сценарий, затем по ссылкам уточняются модель, контракт, preset и governance. Отдельные страницы ниже остаются нормативными приложениями для точных полей и форматов; они не являются самостоятельным объяснением продукта.

Карта 17 тем

1. Модель архитектуры. Опишите source roots, компоненты, стабильные uid, иерархию и классификацию. Результат этого шага — однозначное владение файлами и символами.

2. DSL правил. Запишите kind: architecture, контракт, селекторы, языки, capabilities и coverage. Не смешивайте тип ребра CPG import с capability imports.

3. Каталог контрактов. Выберите один из 12 контрактов: зависимости, слои, циклы, API, полнота классификации, метрика или resolution coverage.

4. Реестр capabilities. Проверьте, какие факты действительно поставляет frontend языка. complete подтверждает обязательные семейства фактов; degraded не разрешает обходить обязательную capability.

5. Создание preset. Добавьте стабильный id@version, manifest capabilities и source-backed fixtures с ожидаемыми исходами. Fixture должен проверять реальный исходный файл, а не только структуру YAML.

6. Каталог presets. Разрешайте preset по точному ID и semantic version. latest и cross-language alias не подходят для enforcement.

7. Baseline. Зафиксируйте известный finding по его fingerprint version, semantic hash правила и точному scope. Изменение смысла правила требует review.

8. Waiver и доверие. Исключение относится к одному finding и сроку. Governance manifest связывает commit, model hash, rules hash, scope, ревизию, подпись и отзыв ключа.

9. Профили release gate. discovery и advisory помогают настроить модель; enforce_new, enforce_all и strict могут блокировать выпуск. Решение должно быть PASS, FAIL_VIOLATIONS или BLOCKED_INCOMPLETE с понятной причиной.

10. Инкрементальное сканирование. Повторное использование допустимо только при совпадении fingerprints, semantic digest и состояний baseline/waiver. Изменения модели, публичного API, ребра или SCC расширяют invalidation.

11. Миграция. Legacy findings сначала идут как discovery/shadow. Перевод в enforcement требует сравнения и двух release cycles; один зелёный локальный запуск этого не заменяет.

12. CLI. validate проверяет применимость, describe показывает модель, explain читает сохранённый canonical finding. Ни одна из этих команд сама по себе не разрешает выпуск.

13. gRPC. Клиент запрашивает revision-bound results по project, run_id и fingerprint. Ответ должен сохранять identity finding и completeness; пустой или смешанный граф не превращается в успешный ответ.

14. JSON Schema. Используйте схемы для обмена результатами, а не как замену описанию процесса. Проверяйте обязательные поля run identity, outcome, completeness, location и lifecycle.

15. SARIF. Экспорт сохраняет rule ID, fingerprint, severity, location и evidence. SARIF удобен для Code Scanning, но release decision остаётся в каноническом результате.

16. Mermaid и PlantUML. Диаграмма показывает модель и связи для ревью. Она не является вторым источником истины и не меняет findings.

17. Диагностика. Начинайте с status и reason code: stale CPG, unknown denominator, missing capability, unresolved edge и untrusted manifest требуют исправления evidence или блокировки enforcement.

Навигация по темам:

Все поля, схемы, ссылки на исходный код и технические проверки теперь собраны в этом руководстве. Общий CI-сценарий выполняет приведённые команды на свежем CLI, проверяет JSON и отрицательные случаи. Одного внутреннего Go-теста недостаточно, чтобы считать пользовательскую процедуру проверенной.

Сейчас каталог v1 содержит три общих шаблона и одиннадцать language presets: 1С, C, C++, C#, Go, Java, JavaScript, Kotlin, PHP, Python и TypeScript. Наличие соседнего языка не доказывает поддержку: capability и fixture qualification проверяются отдельно.

Совместимость и восстановление

Канонические результаты хранятся в gocpg_architecture_results и имеют head в cpg_architecture_result_heads. Миграционный реестр architecture_adoption_ledger.v1 допускает legacy-вывод только как явно помеченную discovery/shadow информацию; legacy_enforcement_allowed=false. Переход к enforcement требует сравнения и two_release_cycles, а не одного успешного локального запуска.

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

Сквозной сценарий принятия решения

В репозитории есть минимальное правило gocpg/docs/examples/architecture-control/architecture.yaml. Оно показывает границы UI и Storage и запрещает прямую зависимость. Сценарий состоит из пяти проверяемых шагов.

  1. Проверить правило. Выполните validate и убедитесь, что applicable=true, gate_evaluated=false, а reason_codes содержит applicable. Это проверка входной конфигурации, не результат аудита.
  2. Проверить модель. Выполните describe --component ui и проверьте стабильный cmp-example-ui. Если компонент не найден, дальнейшие findings нельзя интерпретировать.
  3. Построить CPG. Для отдельного Python-проекта используйте gocpg parse --input ./example-project --output ./cpg.duckdb --lang python. Для PR с историей используйте gocpg ci-update --input . --output cpg.duckdb --base-ref origin/main --head-ref HEAD --json. Результат должен содержать revision и полноту построения.
  4. Оценить policy. Отдельная команда architecture evaluate в CLI не заявлена. Подключите архитектурный анализ к ci-update и передайте четыре поля: policy, ключ проекта, release control и путь к artifact. Выбранная policy задаёт корни исходников, компоненты, правила и классификацию; GoCPG не ищет manifest конкретного проекта. Не подменяйте этот шаг командой validate или внутренним тестом.
  5. Разобрать finding и исправить исходник. Из canonical run возьмите run_id и fingerprint, затем выполните explain. После исправления повторите полный или допустимый incremental run. Если completeness, capability или denominator не доказаны, итогом остаётся BLOCKED_INCOMPLETE.

CI-фрагмент только для CPG

gocpg ci-update --input . --output cpg.duckdb --base-ref origin/main --head-ref HEAD --lang python --json

Без архитектурных полей команда только обновляет CPG. Чтобы запросить архитектурный анализ, передайте все четыре поля:

gocpg ci-update --input . --output cpg.duckdb --base-ref origin/main --head-ref HEAD --lang python --json `
  --architecture-policy config/architecture-policy.yaml `
  --architecture-project-key inventory-service `
  --architecture-release-control config/architecture-release-control.yaml `
  --architecture-artifact artifacts/architecture-control.json
gocpg architecture validate --file gocpg/docs/examples/architecture-control/architecture.yaml --language python --json
gocpg architecture describe --file gocpg/docs/examples/architecture-control/architecture.yaml --component ui --json

CI должен проверить JSON и architecture artifact, а не только код возврата. У validate для неподходящего языка возможен код 0 при applicable=false; это диагностический результат, не разрешение на выпуск. Если отсутствуют все четыре архитектурных поля, policy не запрошена. Неполный набор полей завершается ошибкой. Полный набор запускает выбранную policy; отсутствующий, повреждённый, небезопасный или внешний по отношению к репозиторию input блокируется, а не превращается в успешный skip.

В CI дополнительно выполняется production-тест TestCodeGraphPythonCIPipelineBlocksTheRevisionBoundRESTToMCPWitness. Он читает сущности и зависимости из DuckDB CPG, применяет policy и проверяет, что finding привязан к revision и отражён в release artifact. Это отдельная проверка production-интеграции; она не превращает внутренний тест в замену пользовательским командам выше.

Что считается исправлением

В исходном примере импорт из src/ui в src/storage создаёт finding FAIL_VIOLATIONS. Перенос доступа через разрешённый application component убирает этот finding после нового CPG run. Если импорт невозможно разрешить или классификация неполна, результат не становится зелёным автоматически: его статус BLOCKED_INCOMPLETE, а remediation — восстановить graph facts, capability или governance.

Как выбирать следующий раздел

Если проблема в файлах без владельца, переходите к модели и DSL. Если правило уже нарушается, смотрите контракты, baseline и waiver. Если findings различаются между полным и PR-запуском, проверяйте инкрементальную семантику и capabilities. Если результат нужно передать в CI или Code Scanning, используйте JSON/SARIF и SARIF, сохраняя canonical result как источник решения.