Назад к блогу
Research14 мин чтения

«Не раскрывай себя»: разбираем систему маскировки AI-авторства по винтикам

W
WHOX Research
1 апреля 2026 г.
«Не раскрывай себя»: разбираем систему маскировки AI-авторства по винтикам

Сегодня утёк весь исходный код Claude Code. Полмиллиона строк TypeScript через source maps в npm-пакете. Кто-то не прописал .npmignore. Бронированный сейф, а комбинация написана маркером на стене рядом.

Один файл стал отдельной новостью: utils/undercover.ts.

Когда сотрудник Anthropic запускает Claude Code в публичном репозитории, система принудительно включает Undercover Mode. Отключить нельзя. Промпт, который инжектится в модель:

Ты работаешь ПОД ПРИКРЫТИЕМ в ПУБЛИЧНОМ/OPEN-SOURCE репозитории. Не раскрывай себя.

Убирать Co-Authored-By. Не упоминать Claude Code. Не признаваться что ты ИИ. Писать коммиты как живой разработчик. Компания, которая строит бренд на ответственном ИИ, выкатила в прод отдельный модуль для маскировки своих AI-контрибуций.

Мы двенадцать лет занимаемся фингерпринтингом и детекцией. Динамика знакомая до мурашек. Но вместо очередного горячего комментария мы решили сделать то, что умеем: разобрать маскировку послойно и показать, где она держит и где сыпется.

Большинство комментариев сфокусировались на промпт-инструкциях, девяти правилах, которые говорят модели чего не делать. Мы пошли глубже. Мы прочитали имплементацию: undercover.ts, commitAttribution.ts, claude.ts, coordinatorMode.ts, system.ts. То, что обнаружилось под промптом маскировки, говорит больше, чем сам промпт.

Сами инструкции undercover содержат непринуждённую ошибку. Строка 49 предупреждает модель никогда не упоминать «opus-4-7» или «sonnet-4-8». Это версии моделей, которых публично не существует. Система, спроектированная для предотвращения утечек кодовых имён, сливает кодовые имена в собственном промпте.


Внутри undercover.ts: что раскрывает имплементация

Прежде чем мапить инструкции на слои детекции, стоит посмотреть как Undercover Mode работает на самом деле. Не промпт, который он инжектит, а код, который решает когда, где и почему промпт срабатывает.

Функция активации:

export function isUndercover(): boolean {
  if (process.env.USER_TYPE === 'ant') {
    if (isEnvTruthy(process.env.CLAUDE_CODE_UNDERCOVER)) return true
    return getRepoClassCached() !== 'internal'
  }
  return false
}

Три проектных решения зашиты в этот код, и каждое рассказывает о том, как Anthropic думает о проблеме.

Нет принудительного OFF. Комментарий в исходниках говорит прямо: "There is NO force-OFF. This guards against model codename leaks — if we're not confident we're in an internal repo, we stay undercover." Можно принудительно включить через переменную окружения, но соответствующего выключателя нет. Это дверь в одну сторону. Инженер Anthropic, который хочет быть прозрачным об использовании ИИ в публичной контрибуции, не имеет механизма сделать это через сам инструмент. Система дефолтит в маскировку и не предоставляет opt-out.

По умолчанию: ON. getRepoClassCached() возвращает одно из четырёх значений: 'internal', 'external', 'none' или null (проверка ещё не запускалась). Только 'internal' выключает undercover. Всё остальное, включая «мы ещё не знаем», активирует маскировку. Комментарий в коде: "'external', 'none', and null (check not yet run) all resolve to ON." Anthropic решила трактовать неопределённость так же, как публичную экспозицию. Если сомневаешься, прячься.

Dead-code elimination: маскировка маскировки. Самая архитектурно интересная деталь: USER_TYPE === 'ant' это build-time --define константа. Комментарий: "Since USER_TYPE is a build-time --define, the bundler constant-folds these checks and dead-code-eliminates the ant-only branches from external builds. In external builds every function in this file reduces to a trivial return."

Это значит: в npm-пакете, который устанавливают обычные пользователи, isUndercover() буквально возвращает false, а getUndercoverInstructions() возвращает пустую строку. Система маскировки не просто прячет AI-авторство. Она прячет саму себя. Без source map ты бы никогда не узнал, что эти функции существуют. Маскировка невидима для любого вне Anthropic. Осознанное проектное решение: сделать систему подавления недетектируемой снаружи. Как антидетект-браузеры, которые подменяют отпечатки и одновременно прячут код подмены от JavaScript-инспекции.


Undercover Mode: каждая инструкция против каждого слоя детекции

В утёкшем системном промпте девять конкретных инструкций маскировки. Мы сопоставили каждую с четырьмя слоями детекции из рецензированного исследования (Ghaleb et al., arXiv:2601.17406, январь 2026). Вопрос для каждой: нейтрализует ли инструкция детекционный сигнал?

# Инструкция Undercover Mode Целевой слой Нейтрализует? Почему да / почему нет
1 Не включать строки Co-Authored-By Метаданные коммита Да Метаданные стираются тривиально. Ни один серьёзный детектор на них не опирается.
2 Не упоминать Claude Code и не признаваться что ты ИИ Метаданные коммита Да Поиск по строкам это самый слабый метод детекции.
3 Не использовать внутренние кодовые имена моделей (Capybara, Tengu) Метаданные коммита Да Защищает от утечки интеллекта, к детекции AI отношения не имеет.
4 Не указывать номера невыпущенных версий моделей Метаданные коммита Да Та же категория: IP-защита, не уклонение от детекции.
5 Не упоминать внутренние репозитории, Slack-каналы, короткие ссылки Метаданные коммита Да Та же категория.
6 Писать коммит-сообщения «как написал бы разработчик-человек» Стиль коммит-сообщений Частично Снижает самый информативный сигнал (44,7% глобальной важности). Но «имитация человека» всё равно производит статистические улики: меньшая дисперсия длины сообщений, более однородный тон. Как антидетект-браузер, который подменяет User-Agent, но сохраняет тот же TLS-отпечаток.
7 Никакой атрибуции Метаданные коммита Да Лёгкая победа. Не трогает поведенческие сигналы.
8 Стирать все следы AI из записей коммитов Метаданные + стиль Частично Метаданные: да. Но «следы» в структуре кода (плотность условных операторов, паттерны комментариев) живут не в записях коммитов. Они в diff'е.
9 Нельзя отключить вручную Операционная безопасность Н/Д Страхует от случайного раскрытия. На детекцию не влияет.

Счёт: 6 из 9 инструкций бьют по метаданным. Метаданные это самая слабая поверхность детекции. Два частичных попадания (инструкции 6 и 8) пытаются залезть на территорию поведенческих сигналов, но не дотягиваются до кодовых паттернов, которые несут 55%+ веса детекции.

Исследование говорит об этом без обиняков. Ghaleb et al. обучили XGBoost на 33 580 pull request'ах от пяти агентов: OpenAI Codex, GitHub Copilot, Devin, Cursor, Claude Code. 41 фича. 97,2% F1-score. Характеристики коммит-сообщений доминируют глобально (44,7%), но код-левел фичи вроде плотности условных операторов и паттернов комментариев дают тот отпечаток, который инструкция 6 подавить не может.

Каждый агент течёт по-своему. Codex обожает многострочные коммиты (67,5% важности фичи). Claude Code пихает if/else в каждую щель: 27,2% его сигнала. Copilot строчит описания PR как по тарифу за символы (38,4%). Devin следует conventional commits с такой педантичностью, что это улика (48,9%).

Эти находки сходятся с независимыми исследованиями. Tihanyi et al. (arXiv:2510.10493, 2025) обучили CodeT5-JSA на 250 000 JavaScript-сэмплов от 20 LLM и достигли 95,8% точности на 5-классовой атрибуции моделей, не просто детектируя ИИ, а определяя какая именно модель сгенерировала код. Ключевой вывод: атрибуция переживает минификацию (91% точности), мэнглинг (93%) и даже деобфускацию (85%). Сигналы, которые эксплуатируют классификаторы, это не форматирование и не комментарии, а глубокие структурные паттерны в топологии AST и графах потоков данных. Отпечатки на уровне кода сохраняются через трансформации, которые стирают все поверхностные признаки, доступные системному промпту. Ghaleb определяет что; Tihanyi объясняет почему: отпечаток живёт в том, как модель структурирует логику, а не в том, как форматирует текст.

Методологическая оговорка по Ghaleb et al.: их 33 580 PR полностью сгенерированы агентами. Каждый PR в тренировочном наборе создан целиком AI-агентом, а не с его помощью. Это важно, потому что пороги классификатора (27% плотности условных операторов, 19,8% плотности комментариев) откалиброваны на коде, написанном полностью ИИ. Для смешанных человеко-AI контрибуций (реалистичный сценарий для сотрудников Anthropic) эти пороги могут быть слишком высокими. PR, где Claude написал 60%, а человек переписал 40%, скорее всего упадёт ниже порогов детекции, при этом содержа существенную долю AI-кода. F1-score 97,2% относится к полностью автоматизированным PR, а не к сценарию assisted-coding, который Undercover Mode спроектирован скрывать.


Четыре слоя: куда Undercover Mode не дотягивается

Слой 1: Метаданные коммита. Покрытие Undercover Mode: полное.

Теги атрибуции, упоминания ИИ, внутренние ссылки. Стираются за секунду. Ни один серьёзный детектор на них не полагается. Эквивалент очистки кук с мыслью что ты анонимен.

Слой 2: Стиль коммит-сообщений. Покрытие: частичное.

Тут маскировка начинает буксовать. «Пиши как человек» звучит просто. Статистически это не так.

У людей коммит-сообщения прыгают. Понедельник утром: "fix login." Пятница 18:00: "Переписал middleware авторизации, токен протухал в середине запроса, см. #1247 контекст." AI-генерированные сообщения, даже с инструкцией «звучи как человек», кучкуются в узком диапазоне длин с однородным тоном. Сама дисперсия и есть сигнал.

Ghaleb et al. нашли что характеристики коммит-сообщений несут 44,7% глобального веса детекции. Инструкция 6 («пиши как человек») сужает этот канал, но не закрывает. Как подделка подписи по образцу: форму можно скопировать, но нажим пера и скорость штриха выдают.

Слой 3: Структура кода. Покрытие: ноль.

Ни одна инструкция в утёкшем промпте не адресует паттерны на уровне кода. Ни одна.

Отпечаток Claude Code: повышенная плотность условных операторов (27,2% важности детекции), высокая плотность комментариев (19,8%), однородное именование переменных, равномерная обработка ошибок. Это зашито в веса модели при обучении. Системный промпт «пиши как человек» меняет обёртку. Статистическое распределение выбора токенов, выученное на миллиардах параметров, он изменить не может.

Живой разработчик дрейфует. Именование мутирует внутри файла по мере погружения. Комментарии кучкуются вокруг мест, где пришлось материться. Обработка ошибок разнится от модуля к модулю потому что писалось в разном настроении, в разные дни. AI выдаёт обратное: стерильная консистентность, каждая строка как со штамповочного пресса.

Слой 4: Темпоральные паттерны. Покрытие: ноль.

Смотри git log. Коммит на 487 строк. Через восемь минут фикс опечатки на строку. Ещё три минуты, забытый импорт. Потом патч на валидацию. Этот ритм «сгенерил-латаю» читается как кардиограмма: у человека рваная и непредсказуемая, у AI чистый синус с мелкой рябью коррекций.

Undercover Mode не трогает тайминг коммитов. И не может. Тайминг это функция рабочего процесса, а не промпта. ИИ, который генерирует 500 строк за один проход и потом последовательно патчит edge cases, всегда будет производить другую темпоральную сигнатуру чем человек, который итерирует грязно, вплетая правки в оригинальный код.


Парадокс атрибуции: система, которая измеряет то, что прячет

Вот что никто больше не репортил. Пока undercover.ts прячет AI-авторство снаружи, другой файл (commitAttribution.ts, 962 строки) точно измеряет его изнутри.

Это не logging-утилита. Это полноценная система атрибуции на уровне символов, которая вычисляет ровно сколько каждого коммита написал Claude:

  • Трекает claudeChars vs humanChars для каждого модифицированного файла, используя diff-сопоставление по префиксу/суффиксу для изоляции изменённого региона до конкретных символов
  • Рассчитывает claudePercent по файлу и по коммиту, точный процент AI-авторства
  • Хранит результаты в git notes как структурированный JSON (schema version 1 с раскладкой по файлам, surface info и session ID)
  • Персистирует между сессиями: трекает promptCount, permissionPromptCount, escapeCount, данные переживают compaction и рестарты

Схема данных говорит что именно Anthropic измеряет:

export type AttributionData = {
  version: 1
  summary: {
    claudePercent: number
    claudeChars: number
    humanChars: number
    surfaces: string[]
  }
  files: Record<string, {
    claudeChars: number
    humanChars: number
    percent: number
    surface: string
  }>
  surfaceBreakdown: Record<string, { claudeChars: number; percent: number }>
  excludedGenerated: string[]
  sessions: string[]
}

Каждый модифицированный файл в коммите имеет поля claudeChars, humanChars и percent. Anthropic знает, до символа, сколько каждого файла написано их ИИ.

Парадокс: один и тот же кодовый реестр прячет AI-авторство от мира и точно измеряет его. undercover.ts говорит «никогда не раскрывай что ты такое.» commitAttribution.ts говорит «записывай ровно что ты сделал.» Эти системы сосуществуют в одном репозитории, построенные командами, которые, похоже, не обсуждали противоречие.

Это не лицемерие, а инструментирование. Данные атрибуции остаются во внутренних git notes, не в публичных коммитах. Но это значит что каждый «выглядящий-человеческим» коммит от сотрудника Anthropic в публичном репозитории имеет теневую запись, хранящуюся внутри компании, документирующую точный процент, который написал Claude. Компания не просто прячет сигнал. Она прячет сигнал, сохраняя себе копию.

Для исследования детекции это значимо. Anthropic владеет ground-truth данными об AI-авторском коде в публичных репозиториях: данными, которые можно использовать для оценки систем детекции, улучшения маскировки, или того и другого. Система атрибуции это приватное табло в публичной игре.


22 внутренних репозитория: утечка алловлиста

Undercover Mode деактивируется ровно в одном случае: когда git remote репозитория совпадает с захардкоженным алловлистом в commitAttribution.ts. Утечка раскрывает этот список: 22 приватных репозитория Anthropic, где модели разрешено представляться:

Инфраструктура: terraform-config, infra-manifests, argo-rollouts, starling-configs, mycro_manifests, mycro_configs

Продукты: mobile-apps, claude-for-hiring, forge-web, claude-cli-internal, apps

Внутренние инструменты: casino, dbt, hex-export, ts-tools, ts-capsules, feldspar-testing, trellis, labs

Данные и фидбек: feedback-v2, dotfiles

Комментарий в исходниках объясняет почему это алловлист на уровне репозиториев, а не оргов: "This is intentionally a repo allowlist, not an org-wide check. The anthropics and anthropic-experimental orgs contain PUBLIC repos (e.g., anthropics/claude-code, anthropic-experimental/sandbox-runtime). Undercover mode must stay ON in those to prevent codename leaks. Only add repos here that are confirmed PRIVATE."

Два следствия. Первое: даже собственные публичные репозитории Anthropic получают undercover-обработку. Если инженер Anthropic использует Claude Code для контрибуции в anthropics/claude-code, публичный репозиторий самого инструмента, модель прячет своё участие в собственном исходном коде. Маскировка не различает «наш публичный репо» и «чужой публичный репо». Публичный значит скрытый, вне зависимости от принадлежности.

Второе: алловлист сливает внутренние кодовые имена проектов, которые система была специально спроектирована защищать: «casino», «starling», «feldspar», «trellis», «mycro», «forge». Комментарий к инструкции 5 говорит «Не упоминать внутренние репозитории и проекты.» Алловлист, который обеспечивает это правило, сам является утечкой. Охранник опубликовал список того, что охраняет.

Функция sanitizeModelName в том же файле раскрывает дерево версий моделей Anthropic: opus-4-6, opus-4-5, opus-4-1, opus-4, sonnet-4-6, sonnet-4-5, sonnet-4, sonnet-3-7, haiku-4-5, haiku-3-5. Во внутренних коммитах в алловлистных репозиториях используются сырые идентификаторы моделей; когда код пересекает границу internal/external, они скрабятся до публичных имён. Любая версия, не попавшая в маппинг, фолбечится на общую метку "claude".


Полный pipeline атрибуции: от измерения до стирания

Маскировка не ограничивается undercover-промптом. Отдельный файл attribution.ts реализует полный pipeline от измерения AI-вклада до его стирания.

Когда undercover mode активен, getAttributionTexts() возвращает пустые строки и для коммит-трейлеров, и для PR-описаний:

export function getAttributionTexts(): AttributionTexts {
  if (process.env.USER_TYPE === 'ant' && isUndercover()) {
    return { commit: '', pr: '' }
  }
  // ...
  const defaultCommit = `Co-Authored-By: ${modelName} <noreply@anthropic.com>`
  const defaultAttribution = `🤖 Generated with [Claude Code](${PRODUCT_URL})`
  // ...
}

Когда undercover OFF (внутренние репозитории), система генерирует Co-Authored-By трейлеры и PR-бейджи. Когда ON, оба пустые. Переключатель бинарный: полная атрибуция или нулевая. Никакого частичного раскрытия, никакого промежуточного «AI-assisted».

Внутренние трейлеры атрибуции ещё красноречивее. Комментарий на строке 837 commitAttribution.ts объясняет: "formatAttributionTrailer moved to attributionTrailer.ts for tree-shaking (contains excluded strings that should not be in external builds)." Код форматирования трейлеров был физически вынесен в отдельный файл специально чтобы бандлер мог вырезать его из публичных билдов. Внутренние коммиты несут трейлеры вроде «93% 3-shotted by claude-opus-4-6», но код, который их генерирует, не существует ни в одном npm-пакете, который может установить пользователь.

Это третий слой маскировки: система атрибуции измеряет AI-вклад (слой один), undercover mode подавляет его в коммитах (слой два), tree-shaking удаляет код отображения измерений из артефакта сборки (слой три). Даже реверс-инжиниринг npm-бандла не раскроет формат трейлеров, потому что кода буквально нет.


Проверяем: сотрудники Anthropic в дикой природе

Теория дёшево стоит. Мы посчитали.

Мы написали автоматизированный pipeline, собирающий публичные PR от подтверждённых сотрудников Anthropic, участников GitHub-организации anthropics и топ-контрибуторов репозитория anthropics/claude-code, и вычисляющий пять метрик детекции на каждый PR: плотность условных операторов, плотность комментариев, соотношение What:Why комментариев, дисперсия именования идентификаторов (σ) и дисперсия длины коммит-сообщений (σ). Мы проанализировали 28 вмёрженных PR от 8 сотрудников Anthropic в 18 внешних open-source репозиториях.

Автор Репо Строк Cond% Com% W:W ID σ Коммитов Msg σ CoAuth
RobertCraigie NixOS/nixpkgs 1465 0.0% 1.0% 15:0 — 1 — 0
RobertCraigie glide-browser/glide 740 1.6% 14.2% 105:0 14.17 4 0.5 0
karpetrosyan crate-py/url #201 232 0.9% 9.1% 21:0 5.77 1 — 0
karpetrosyan openai/openai-python 92 0.0% 3.3% 3:0 — 7 2.5 0
karpetrosyan crate-py/url #200 49 0.0% 12.2% 6:0 3.54 1 — 0
mikelambert mastra-ai/mastra 81 2.5% 0.0% 0:0 6.65 9 1.9 7
mikelambert ag2ai/ag2 56 0.0% 5.4% 3:0 — 6 1.2 1
mikelambert lobehub/lobehub 51 0.0% 2.0% 1:0 1.15 1 — 1
mikelambert ThinkInAIXYZ/deepchat 42 0.0% 9.5% 4:0 — 1 — 1
mikelambert anything-llm 8 0.0% 0.0% 0:0 — 5 1.3 1
lovesegfault nix-community/crate2nix 163 3.1% 11.0% 16:2 — 2 1.4 0
lovesegfault NixOS/nixpkgs #502733 39 2.6% 15.4% 6:0 — 1 — 0
lovesegfault NixOS/nixpkgs #502704 33 3.0% 21.2% 7:0 — 1 — 0
felixrieseberg electron/fiddle #1851 1315 0.2% 6.2% 81:1 3.90 5 1.5 3
felixrieseberg electron/electron 16 100% 0.0% 0:0 — 1 — 0
felixrieseberg MarshallOfSound/ipc 23 13.0% 13.0% 3:0 1.73 2 1.4 1
bcherny zed-industries/zed #19928 354 3.7% 0.3% 1:0 5.15 7 1.6 2
bcherny zed-industries/zed #18640 83 7.2% 0.0% 0:0 4.60 10 2.3 1
chrislloyd oven-sh/bun 43 0.0% 2.3% 1:0 2.65 2 1.4 1
Mic92 numtide/llm-agents.nix 48 0.0% 29.2% 13:1 — 3 2.1 0
ddworken modelcontextprotocol/python-sdk — — — — — — — 0

(Плюс 7 микро-PR менее 10 строк, исключённых из анализа. Полный датасет: 28 PR, 8 авторов, 18 репозиториев. «—» = недостаточно данных для метрики.)

Агрегированная статистика

По существенным PR (≥10 добавленных строк, n=21):

Метрика Сотрудники Anthropic AI-зона (Ghaleb et al.)
Плотность условных операторов (среднее) 7.0% (медиана 0.9%) >27%
Плотность комментариев (среднее) 11.0% (медиана 6.2%) >19.8%
ID naming σ (n=6, ≥5 идентификаторов) 6.71 (медиана 5.46) <2.0
Commit msg σ (n=12, ≥2 коммитов) 1.6 (медиана 1.4) <1.0
Соотношение What:Why комментариев 302:5 (60:1) высокое what:why

Ни один PR не триггерит ≥2 флагов Ghaleb одновременно. Ни одна из этих контрибуций не выглядит AI-сгенерированной по стандартам классификатора. Плотность условных операторов в среднем 7.0% против порога 27%; даже медиана (0.9%) на порядок ниже флага. Разнообразие именования идентификаторов высокое (σ 6.71 vs <2.0 AI-зона). Дисперсия коммит-сообщений здоровая.

Один сигнал выделяется: соотношение What:Why составляет 302:5 по всем 28 PR. Только в 4 PR есть хоть один why-комментарий. 60 what-комментариев на каждый why. Это AI-паттерн: он объясняет что код делает, никогда не объясняет почему он существует. Но это также паттерн маленьких утилитарных контрибуций, где контекст очевиден. С большим датасетом существенных PR (>200 строк) это соотношение было бы более диагностичным.

Бомба Co-Authored-By

А вот тут начинается самое интересное. Наш автоматизированный pipeline зафиксировал теги Co-Authored-By по всему датасету. Мы ожидали ноль: инструкция #1 Undercover Mode требует их стирать. Вместо этого: 10 из 28 PR (36%) содержат явные теги AI-соавторства. 19 из 78 общих коммитов (24%) указывают Claude по имени.

Мы извлекли точные строки соавторства из коммит-сообщений:

  • Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>: mikelambert (mastra, lobehub, anything-llm, ag2, deepchat), felixrieseberg (electron/fiddle, MarshallOfSound/ipc), chrislloyd (oven-sh/bun)
  • Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>: chrislloyd (oven-sh/bun, более ранний коммит)
  • Co-authored-by: Boris <boris@anthropic.com>: bcherny (zed-industries/zed, парное программирование с другим сотрудником Anthropic)

Восемь из десяти тегов Co-Authored-By явно указывают Claude Opus 4.5 или 4.6 как AI-соавтора. Сотрудники Anthropic открыто кредитуют собственную AI-модель в публичных open-source коммитах.

Почему это происходит? Исходный код объясняет всё полностью. isUndercover() проверяет process.env.USER_TYPE === 'ant', build-time константу. Стандартный npm-пакет поставляется с USER_TYPE равным 'external', что означает: весь файл undercover.ts это мёртвый код в публичных билдах. Каждая функция возвращает тривиальное значение. getAttributionTexts() спокойно генерирует Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>, потому что нет undercover mode, который бы это подавлял.

Эти сотрудники используют стандартный билд Claude Code из npm, тот же, что может установить любой. Не внутренний билд Anthropic с USER_TYPE='ant'. Система маскировки существует только во внутренних билдах, и как минимум четыре сотрудника Anthropic (mikelambert, felixrieseberg, bcherny, chrislloyd) активно контрибутят в публичные репозитории с полной AI-атрибуцией, используя стандартный билд.

Разящая ирония: тот же кодовый реестр, который реализует систему маскировки специально для сотрудников Anthropic, содержит и другую систему, производящую именно те метаданные, которые Undercover Mode спроектирован стирать. Какая система сработает, зависит от билда. А сотрудники, судя по всему, используют стандартный.

Оставшиеся 18 PR без Co-Authored-By могут быть: (a) сотрудники на внутреннем билде с активным Undercover Mode, (b) PR с одним коммитом без участия ИИ, или (c) контрибуции без Claude Code. Различить эти случаи по данным коммитов мы не можем. Но 36% открытой AI-атрибуции от сотрудников Anthropic делает вопрос «тайно ли они используют ИИ?» частично неактуальным. Треть из них говорит об этом прямо.

Размер как поверхность уклонения

61% из 28 PR это менее 50 строк. Только 21% превышают 100 строк. Это консистентно по всем восьми сотрудникам. Классификатор Ghaleb et al. обучался на полных PR, сгенерированных агентами от начала до конца; маленькие контрибуции могут просто лежать ниже статистического порога. Не потому что чистые, а потому что кода недостаточно для сигнала. Нельзя снять отпечаток пальца с размазанного пятна на дверной ручке.

Намеренная ли это стратегия или естественное контрибуционное поведение, эффект одинаков: маленькие, скоупированные PR это структурное слепое пятно текущих классификаторов. Если ты знаешь что крупные PR ловятся с точностью 97,2%, рациональный ход это то, что мы видим: держать контрибуции маленькими, скоупированными и незаметными.


Антидистилляция: второй слой маскировки

Undercover Mode прячет кто написал код. Отдельная система в claude.ts прячет как модель думает и отравляет любого, кто пытается учиться на её выводе.

Строки 301-313 реализуют антидистилляцию. Когда совпадают четыре условия (compile-time флаг ANTI_DISTILLATION_CC, CLI entrypoint, first-party API provider и GrowthBook feature gate tengu_anti_distill_fake_tool_injection), API-запрос включает anti_distillation: ['fake_tools']. Сервер отвечает инжекцией фейковых определений инструментов в системный промпт. Если кто-то записывает API-трафик Claude Code для обучения конкурирующей модели, фейковые инструменты загрязняют тренировочные данные.

Второй механизм в betas.ts добавляет серверную суммаризацию connector-текста. Между tool calls API буферит рассуждения ассистента, суммаризирует их и возвращает саммари с криптографической подписью. На следующих шагах оригинальный текст может быть восстановлен из подписи. Если вы записываете трафик для дистилляции конкурирующей модели, вы получаете саммари, а не цепочки рассуждений.

Нативная клиентская аттестация в system.ts добавляет третий слой. API-запросы включают плейсхолдер cch=00000 в заголовке биллинга. Прежде чем запрос покинет процесс, нативный HTTP-стек Bun (написанный на Zig) перезаписывает эти пять нулей вычисленным хешем. Сервер верифицирует этот токен для подтверждения что запрос пришёл от настоящего бинарника Claude Code. Плейсхолдер использует ту же длину в байтах чтобы избежать изменений Content-Length и перевыделения буфера. Вычисление происходит ниже JavaScript-рантайма, невидимое для чего-либо в JS-слое. Это DRM для API-вызовов, реализованная на уровне HTTP-транспорта.

Вместе с Undercover Mode они формируют трёхслойную архитектуру маскировки: спрятать кто написал код (undercover), спрятать как модель рассуждает (антидистилляция) и верифицировать что клиент настоящий (аттестация). Каждый слой адресует свою поверхность угроз.


Информационная асимметрия: каждый запрос помечен, каждый автор скрыт

Пока undercover mode стирает AI-следы из коммитов, параллельная система в том же кодовом реестре помечает каждый API-запрос идентифицирующими метаданными. Обе системы вызываются из одного места в claude.ts:

const fingerprint = computeFingerprintFromMessages(messagesForAPI)
// ...
systemPrompt = asSystemPrompt([
    getAttributionHeader(fingerprint),
    // ...
])

Вычисление фингерпринта в fingerprint.ts использует захардкоженный солт 59cf53e54c78, символы на позициях [4, 7, 20] первого сообщения пользователя и строку версии для генерации 3-символьного hex-хеша:

export const FINGERPRINT_SALT = '59cf53e54c78'

export function computeFingerprint(messageText: string, version: string): string {
  const indices = [4, 7, 20]
  const chars = indices.map(i => messageText[i] || '0').join('')
  const fingerprintInput = `${FINGERPRINT_SALT}${chars}${version}`
  const hash = createHash('sha256').update(fingerprintInput).digest('hex')
  return hash.slice(0, 3)
}

Функция getAttributionHeader в system.ts комбинирует этот фингерпринт с версией, entrypoint, хешем клиентской аттестации и маршрутизацией нагрузки в единый заголовок: x-anthropic-billing-header: cc_version=VERSION.FINGERPRINT; cc_entrypoint=cli; cch=00000;. Каждый API-вызов несёт этот заголовок. В том же запросе, если сотрудник использует внутренний билд, getAttributionTexts() в commit.ts возвращает пустые строки для коммит-трейлера.

Архитектура не парадоксальна. Она намеренно асимметрична. Биллинговый заголовок идёт на серверы Anthropic. Коммит-трейлер идёт в публичный репозиторий. Две аудитории, две политики: полная трассировка для сервера, нулевая атрибуция для мира. Система не противоречит себе. Она обслуживает разных хозяев в одном HTTP-запросе.

В сочетании с посимвольным трекингом commitAttribution.ts Anthropic владеет полной цепочкой: какая сессия сгенерировала какой код, какая версия модели, какой процент написан ИИ и криптографический фингерпринт, привязывающий запрос к содержимому сообщения пользователя. Компания, которая стрипает Co-Authored-By из ваших коммитов, знает о том кто что написал больше, чем любой внешний детектор когда-либо сможет.

Ещё одна деталь из secretScanner.ts кристаллизует архитектуру. Сканер, проверяющий утечки API-ключей, собирает префикс ключа Anthropic как ['sk','ant','api'].join('-') в рантайме, потому что буквальная строка sk-ant-api запрещена их собственной проверкой excluded-strings. Сканер обходит собственный сканер. Система, детектирующая утечки, сама спроектирована чтобы избежать детекции системой, предотвращающей утечки. Рекурсивная маскировка.


Контекст индустрии: Anthropic против всех остальных

Undercover Mode существует не в вакууме. Каждый крупный AI-инструмент для кодинга сделал выбор в отношении прозрачности атрибуции, и выбор Anthropic стоит особняком.

GitHub Copilot добавляет Co-Authored-By: Copilot к коммитам по умолчанию. С марта 2026 года он также включает трейлер Agent-Logs-Url, связывающий каждый коммит с полным логом сессии для аудита. Атрибуция включена по умолчанию, отключается через конфигурацию. Политика: сначала прозрачность, с возможностью отказа.

Cursor выпустил Agent Trace в январе 2026 года, открытую спецификацию (RFC v0.1.0) для отслеживания AI-сгенерированного кода на уровне файлов и строк с четырьмя классификациями контрибуторов: human, ai, mixed, unknown. Спецификацию поддержали Cognition (Devin), Google Jules, Cloudflare и Vercel. Cursor добавляет «Made with Cursor» к коммитам по умолчанию и предлагает построчный Cursor Blame на Enterprise-планах. Атрибуция включена по умолчанию, отключается в настройках.

Amazon CodeWhisperer идёт другим путём: референс-трекер, который флажит предложения, похожие на open-source тренировочные данные, показывая URL репозитория и лицензию. Нет атрибуции на уровне коммитов, но проактивная IP-прозрачность.

Паттерн очевиден. Copilot, Cursor и CodeWhisperer все дефолтят в ту или иную форму раскрытия. Ни один не поставляет движок маскировки. Anthropic это единственный вендор, чей инструмент активно подавляет AI-атрибуцию, и единственный, кто делает это без возможности отказа.

Kraishan (arXiv:2512.00867, ноябрь 2025) исследовал 14 300 GitHub-коммитов в 7 393 репозиториях и обнаружил, что стандартные пользователи Claude Code атрибутируют в 80,5% случаев, самый высокий показатель среди всех инструментов (Copilot: 9,0%). Стандартный билд без Undercover Mode это самый прозрачный инструмент в экосистеме. Система маскировки существует исключительно во внутреннем билде, используемом сотрудниками Anthropic. Компания построила самую прозрачную систему AI-атрибуции в индустрии, а потом построила вторую систему, чтобы отключить её для себя.


Coordinator Mode: мозг это промпт

Ещё один файл, который стоит рассмотреть. coordinatorMode.ts раскрывает как Claude Code управляет многоагентными рабочими процессами, и ответ архитектурно удивителен: алгоритм оркестрации это не код. Это системный промпт на 4000+ слов.

Координатор управляет рабочими агентами через инструкции на естественном языке: "Do not rubber-stamp weak work." "You must understand findings before directing follow-up work. Never hand off understanding to another worker." "Verification means proving the code works, not confirming it exists."

Маршрутизация задач, решения о параллелизме, контроль качества и восстановление после сбоев, всё это промпт. Никаких стейт-машин, алгоритмов планировщика, очередей приоритетов. «Мозг» многоагентной системы Claude Code это текст. Значит: любой с промптом и API-ключом может реплицировать оркестрацию. Координация не защищена сложностью кода. Это просто очень длинный, очень конкретный набор инструкций.

Для истории детекции координатор важен не маскировкой, а консистентностью. Каждый пользователь Claude Code в режиме координатора получает одну и ту же оркестрационную логику: тот же промпт, ту же маршрутизацию задач, те же паттерны верификации. Эта однородность это именно тот тип сигнала, который классификаторы учатся эксплуатировать: не что делает код, а как работа структурирована между коммитами.


Аудит своего репозитория

Не нужен ML-классификатор чтобы проверить репозиторий на AI-отпечатки. Git log и базовые консольные инструменты дают пять сигналов, соответствующих фичам из paper'а Ghaleb et al.

1. Дисперсия длины коммит-сообщений

git log --format="%s" --no-merges | awk '{print NF}' | \
  awk '{sum+=$1; sumsq+=$1*$1; n++} END {
    mean=sum/n; 
    variance=sumsq/n - mean*mean; 
    print "Mean words:", mean, "Std dev:", sqrt(variance)
  }'

Низкое стандартное отклонение (ниже 3.0 слов) при средней 6-12 слов: подозрительно. Люди бросаются от однословных сообщений к абзацам. ИИ кучкуется посередине.

2. Темпоральная каденция

git log --format="%H %at" --no-merges | awk '
  NR>1 {
    delta = prev_time - $2;
    if (delta > 0 && delta < 3600) 
      print prev_hash, delta "s"
  }
  { prev_time=$2; prev_hash=$1 }
'

Ищи паттерн: крупный коммит, за ним 2-3 мелких в течение 15 минут. Отношение «большой коммит → серия микрофиксов за 15 мин» к самостоятельным коммитам: выше 30% это флаг.

3. Плотность условных операторов (по PR/коммиту)

git diff <commit> | grep -c '^\+.*\(if\|else\|switch\|case\|? .*:\)' 
git diff <commit> | grep -c '^\+' 
# Разделить первое на второе. Выше 0.15 для невалидационного кода: флаг.

Фирменный почерк Claude Code: if/else везде. Люди пишут условия когда логика требует. ИИ фронтлоадит их как дефолтный паттерн управления потоком, выдавая повышенную плотность даже там где ветвление не нужно.

4. Распределение комментариев

git diff <commit> | grep '^\+.*\/\/' | head -20

Ручная проверка: комментарии через равные интервалы (каждые 5-10 строк) или кластерами вокруг сложных участков? Объясняют что ("Хешируем пароль") или почему ("bcrypt вместо argon2 потому что на проде нет libsodium")? Равномерное распределение + what-комментарии: AI-сигнатура.

5. Консистентность именования

git diff <commit> | grep -oE '^\+.*(let|const|var|function)\s+[a-zA-Z_]+' | \
  grep -oE '[a-zA-Z_]+$' | awk '{print length}' | \
  awk '{sum+=$1; sumsq+=$1*$1; n++} END {
    mean=sum/n;
    print "Mean length:", mean, "Std dev:", sqrt(sumsq/n - mean*mean)
  }'

Стандартное отклонение ниже 2.5 символов на 20+ идентификаторов: подозрительно однородно. Люди используют короткие имена для переменных цикла, длинные для доменных объектов, и всё между.

Оценка: Три или больше флагов на одном PR это сильный сигнал. Ни одна метрика сама по себе не доказательство. Сила в корреляции между слоями. Тот же принцип что в кросс-слойном браузерном фингерпринтинге.


Архитектурные ограничения

Дизайн системы маскировки раскрывает структурные лимиты, которые никакое обновление промпта не исправит.

Ограничение 1: системные промпты не могут изменить распределение токенов. Каждая поведенческая модификация, добавленная в undercover-промпт, создаёт паттерн второго порядка. Инструкция «варьируй плотность условных операторов» производит искусственно варьированную плотность: распределение, оптимизированное под цель, которая отличается от естественного распределения человеческого кода. Зазор между «оптимизированным-под-похожесть-на-человека» и «настоящим человеком» это ровно то, что классификаторы следующего поколения научатся эксплуатировать. Tihanyi et al. продемонстрировали это напрямую: атрибуция переживает даже тяжёлые трансформации кода, потому что классификаторы захватывают паттерны потоков данных и AST-уровня, зашитые в веса модели, а не поверхностное форматирование, которым управляют промпты.

Ограничение 2: кросс-слойная консистентность экспоненциально сложна. Мы наблюдали этот цикл десять лет на поле браузерного антидетекта. Подменить User-Agent занимает пять секунд. Рандомизировать canvas занимает полдня. Добиться кросс-слойной консистентности, где каждый наблюдаемый параметр рассказывает связную историю? Тут большинство решений ложатся, потому что каждый залатанный слой обнажает три, о которых ты не знал. Undercover Mode адресует два из четырёх слоёв детекции (метаданные и коммит-сообщения), оставляя два (структура кода и темпоральные паттерны) полностью нетронутыми. Добавление промпт-инструкций для структуры кода создаёт пятое наблюдаемое измерение (compliance с промптом), не адресуя темпоральные паттерны. Слои множатся быстрее, чем заплатки.

Ограничение 3: архитектура структурно фрагментирована. Исходный код не содержит общей конфигурации, единой политики и координирующего слоя, связывающего undercover.ts (подавить атрибуцию) с commitAttribution.ts (измерить атрибуцию) с attribution.ts (отформатировать или стереть атрибуцию) с fingerprint.ts (пометить каждый запрос). Четыре файла, четыре поведения, ни одного координатора. Было ли это осознанным разделением задач или организационным пробелом, результат одинаков: система, которая прячет, и система, которая измеряет, работают независимо, производя противоречия, которые сам исходный код документирует.

Куда движется детекция. Траектория исследований ясна: от бинарной классификации (человек/ИИ) к атрибуции моделей (какой именно ИИ), и от статических классификаторов к темпоральным моделям, которые отслеживают поведенческий дрейф по истории контрибутора. Отпечаток не в отдельном коммите. Он в последовательности. 20-классовая атрибуция Tihanyi et al. с точностью 88,5% демонстрирует, что даже различение между близкородственными моделями (GPT-4o vs GPT-4o-mini vs GPT-5) это решаемая задача. Вопрос смещается от «написано ли это ИИ?» к «какая конкретно модель, какая версия, какая конфигурация это написала?»


Что Undercover Mode на самом деле рассказал

Самое интересное в utils/undercover.ts не маскировка. Это признание.

Anthropic знает что их ИИ оставляет детектируемые следы в коде. Они оценили риск достаточно точно чтобы построить отдельную систему подавления. Скоупнули подавление на метаданные и коммит-сообщения, потому что это слои, которые можно контролировать через системный промпт. Структуру кода и темпоральные паттерны не тронули, потому что для этого пришлось бы переобучать модель.

Но исходный код говорит больше, чем промпт когда-либо мог. Семь взаимосвязанных систем, каждая адресует свою угрозу:

  • undercover.ts: спрятать кто написал код
  • commitAttribution.ts: измерить ровно сколько написал ИИ
  • attribution.ts + attributionTrailer.ts: отформатировать измерение для внутреннего использования, стереть для внешнего, с tree-shaking для удаления кода форматирования из публичных билдов
  • Алловлист внутренних репозиториев: классифицировать какие контексты безопасны для честности
  • Dead-code elimination: спрятать саму систему маскировки от внешних билдов
  • Антидистилляция в claude.ts: спрятать как модель рассуждает и отравить копии
  • fingerprint.ts + system.ts: фингерпринтить каждый API-запрос контентным фингерпринтом и клиентской аттестацией, сохраняя серверную прослеживаемость при стирании клиентской атрибуции

Каждая система работает. Вместе они формируют карту всего, что Anthropic считает поверхностью угроз, опубликованную командой, которая её построила, в source map, которую забыли исключить.

И наш эмпирический анализ добавляет восьмую точку данных: как минимум четыре сотрудника Anthropic открыто кредитуют Claude Code в публичных коммитах, используя стандартный билд и обходя всю архитектуру маскировки. Система, прячущая AI-авторство, сосуществует с сотрудниками, которые не утруждаются его прятать. Либо Anthropic не обязала использовать внутренние билды для внешних контрибуций, либо сотрудники сделали личный выбор в пользу прозрачности, который архитектура компании пыталась сделать за них.

Разрыв между тем что Undercover Mode покрывает и тем что нет это карта известной поверхности детекции Claude Code. Система атрибуции добавляет вторую карту: что Anthropic измеряет внутри говорит какие сигналы они считают реальными. Алловлист добавляет третью: в каких репозиториях им хватает доверия быть честными говорит где они видят риск. Данные Co-Authored-By добавляют четвёртую: какие сотрудники используют стандартные билды говорит как далеко на самом деле распространяется маскировка.

Для тех кто строит детекцию AI-кода, это не просто подарок. Это чертёж. Цель сама рассказала какие слои считает уязвимыми, от каких отказалась, какие трекает для собственного табло и какие сотрудники вышли из маскировки целиком.

Прикрытие было раскрыто до того, как его построили.


WHOX - Отличаем настоящие сигналы от сфабрикованных. Проверьте свой отпечаток на whox.com и узнайте какие сигналы утекают из вашего браузера и насколько вы уникальны.

Источники

Ghaleb, T.A. et al. "Fingerprinting AI Coding Agents on GitHub." arXiv:2601.17406, январь 2026. MSR 2026 Mining Challenge.

Tihanyi, N. et al. "The Hidden DNA of LLM-Generated JavaScript: Structural Patterns Enable High-Accuracy Authorship Attribution." arXiv:2510.10493, 2025.

Kraishan, O. "The AI Attribution Paradox: Transparency as Social Strategy in Open-Source Software Development." arXiv:2512.00867, ноябрь 2025.

Lu, Y. et al. "WASA: Watermarking for Source Code Attribution." ACL Findings, 2025.

Утечка исходного кода Claude Code, 31 марта 2026. Проанализированные файлы: utils/undercover.ts, utils/commitAttribution.ts, utils/attribution.ts, coordinator/coordinatorMode.ts, services/api/claude.ts, constants/system.ts, utils/fingerprint.ts, services/teamMemorySync/secretScanner.ts, utils/http.ts, commands/commit.ts.

Данные GitHub API собраны 1 апреля 2026: 28 PR от 8 подтверждённых сотрудников Anthropic в 18 внешних репозиториях. Pipeline анализа: автоматизированная экстракция метрик через GitHub REST API с кешированными ответами.

ai-detectionclaude-codefingerprintingresearchcode-authorship

Похожие статьи