You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Em uma frase: hoje o Iris só reconhece um commit como IA se ele tiver
um trailer no texto; commits autorados diretamente por um agente
(Claude, Copilot coding agent, Devin, Cursor Agent) caem em HUMANO ou BOT,
contaminando a baseline humana ou sumindo da comparação IA × humano.
Referências arquivo:linha apontam para main @ da16235 (antes desta
mudança). Esta issue é independente: aplica, testa e valida sozinha sobre main. Toda preocupação levantada na análise adversária tem disposição
final — decidida, mitigada, transformada em critério de aceite desta issue,
convertida em condição ("ao mesclar…") ou desmembrada em PR separada.
Hipótese
Essa métrica nos ajuda a entender como IA muda software delivery porque toda comparação IA × humano do Iris depende de classificar corretamente
quem escreveu cada commit — e a forma dominante de autoria por IA está
mudando de assistência (pessoa commita, IA co-assina) para agência (o
agente commita como autor, sem trailer). Se esses commits entram na baseline
humana, a diferença IA × humano encolhe artificialmente; se entram em BOT,
os commits mais "IA" de todos somem de durability_by_origin, fix_target_by_origin, cascade_rate_by_origin e acceptance_by_origin,
que iteram só HUMAN e AI_ASSISTED.
O registro de qual evidência identificou cada commit responde a uma
pergunta operacional que hoje não tem resposta: quanto da nossa visibilidade
de IA depende de o hook prepare-commit-msg estar instalado?
Definição
Comportamento atual (o problema)
detect_tool() lê apenas commit.attribution_trailers
(iris/analysis/origin_classifier.py:132-136). O campo Commit.author_email
existe (iris/models/commit.py:30) e é preenchido pelo git_reader
(iris/ingestion/git_reader.py:243), mas nunca é consultado. Teste sintético
no classificador real, main @ da16235:
O padrão da primeira linha é exatamente o da Fig. 1 do paper (repositório claudes-c-compiler: 3.957 commits com Claude como autor, ~500K linhas
em semanas).
Comportamento proposto
Ordem de avaliação em classify_origin() — primeiro match vence:
TrailerCo-authored-by / Assisted-by / Made-with nomeia uma
ferramenta de _AI_TOOL_PATTERNS → AI_ASSISTED, evidência trailer. (Inalterado.)
Domínio do e-mail do autor está em _AI_EMAIL_DOMAINS (match por
domínio ou subdomínio, nunca por padrão de texto) → AI_ASSISTED,
evidência author_email. A lista contém apenas anthropic.com — o
único domínio citado pelo paper; entradas novas só com commit observado.
Nome do autor carrega marcador de agente ([bot] ou a palavra Agent, delimitada por não-alfanuméricos) e nomeia uma ferramenta de _AI_TOOL_PATTERNSe não está em _BOT_NAMES → AI_ASSISTED,
evidência author_name.
_BOT_AUTHOR_PATTERNS casa o nome → BOT. (Inalterado — o human_review_coverage.py:43,175 importa esse regex diretamente e não é
afetado.)
Novo campo em ReportMetrics (iris/models/metrics.py, após commit_origin_distribution):
origin_evidence_distribution: dict[str, int] | None
# ex.: {"trailer": 68} · {"trailer": 40, "author_email": 12, "author_name": 9}
# None quando não há commits AI_ASSISTED
Populado em iris/metrics/aggregator.py logo após ai_detection_coverage_pct,
iterando origin_classified e contando a evidência dos AI_ASSISTED.
Emitido automaticamente por ReportMetrics.to_dict() (asdict). Aceito
pela rota /api/ingest sem mudança de contrato: o schema zod usa .passthrough() (platform/src/app/api/ingest/route.ts:45) e o OpenAPI
declara additionalProperties: true (platform/openapi.yaml:489); o payload
é persistido em JSONB (platform/supabase/migrations/008_iris_tables.sql:42).
Render no relatório (iris/reports/writer.py, bloco de cobertura de IA, :262-272): a frase de evidência é anexada à linha de cobertura existente,
ordenada por contagem decrescente — chaves i18n ai_evidence_breakdown, evidence_trailer, evidence_author_email, evidence_author_name em EN e
PT-BR:
> AI detection coverage: 100% of commits have AI attribution. Attribution evidence: 67 via trailer.
> Cobertura de detecção AI: 100% dos commits possuem atribuição AI. Evidência de atribuição: 67 via trailer.
não muda (usa _BOT_AUTHOR_PATTERNS em autores de review)
Fonte do sinal
múltiplas fontes — git log (nome e e-mail do autor) + co-author / trailers
do hook. Todas offline; nenhuma chamada de rede nova.
Risco de ranqueamento individual
Baixo e não-novo. A classificação é por classe de origem (HUMAN /
AI_ASSISTED / BOT), não por pessoa; origin_evidence_distribution é um
agregado por repositório com três chaves, sem nomes. A regra 3 só se aplica a contas de agente (marcador [bot]/Agent), nunca a pessoas — e a regra
2 casa por domínio de e-mail de vendor, o que exclui pessoas por
construção ([email protected] continua humano). Nenhum campo novo
carrega identidade.
python schema (iris/models/metrics.py) — campo novo, None por padrão
report writer (iris/reports/writer.py) — frase de evidência na linha de cobertura; chaves i18n EN + PT-BR; testado em test_writer_evidence_breakdown.py
narrative findings (iris/reports/narrative.py) — decidido: não se aplica. A distribuição é descritiva; não há threshold natural que separe "bom" de "ruim" (um repo 100% trailer e um repo 100% agente são ambos legítimos). Um finding aqui seria opinião, não achado.
TS types (platform/src/types/metrics.ts) — campo opcional adicionado; tsc, vitest, eslint, prettier e next build verdes
platform UI — decidido: adiado por desenho.CLAUDE.md (Design Philosophy): "A new UI view must be justified by a metric that already tells a useful story in the engine." Nenhum repo real ainda mostrou uma distribuição com mais de uma fonte de evidência; quando o dry run do critério de aceite mostrar uma, o painel de cobertura de IA é o lugar natural (sem view nova).
docs/METRICS.md atualizado — linha em §4 + mapa "Module → fields"
CHANGELOG — passo do release (CLAUDE.md, Release Checklist, item 4), não pré-requisito de merge. Deve registrar a mudança de semântica (ver adversária, ataque 4).
Critérios de aceite (antes do merge)
Dry run do engine com este patch em ≥ 1 repositório da org que tenha
commits autorados por agente (Copilot coding agent, Devin, Cursor
background agent ou Claude autor). Anexar à PR o commit_origin_distribution e o origin_evidence_distribution antes e
depois. Aqui o delta é zero por construção (todo commit de IA tem
trailer) — a cobertura desse cenário é por teste de integração, e um
dry run real é o que confirma o comportamento em campo.
CI verde em ambos os jobs (cli e platform).
PR descreve a mudança de semântica para o CHANGELOG do próximo release.
Ao mesclar
Dois itens nascem depois do merge, não antes — abri-los agora seria
criar issues especulativas:
[DEBT] de anotação por cli_version em platform/lib/queries/temporal.ts: a reclassificação muda commit_origin_distribution e ai_detection_coverage_pct para o mesmo
histórico, e a detecção período-a-período do dashboard não consulta a
versão da CLI (o payload carrega cli_version, iris/platform/push.py:35;
a rota persiste, route.ts:153). Abrir com a descontinuidade real
observada na primeira run ingerida após o release.
PR pequena no hook: prepare_commit_msg.sh passa a detectar por env
var as ferramentas que a org usa além do Claude Code, e ganha um teste de
paridade entre o guard do hook e _AI_TOOL_PATTERNS.
Notas / prior art
Paper — Y. Liu, R. Widyasari, Y. Zhao, I. C. Irsan, J. Chen, D. Lo, Debt Behind the AI Boom: A Large-Scale Empirical Study of AI-Generated Code
in the Wild, arXiv:2603.28592v2 [cs.SE], 2026. §III-A "AI Attribution
Rules", p.3: quatro fontes de evidência em metadata do Git — (1) actor
login (ex. copilot-swe-agent[bot]), (2) author email (ex. [email protected]), (3) author name (ex. Cursor Agent), (4) trailer Co-authored-by. 29 ferramentas deixaram rastros identificáveis. §IV-D,
p.6: 100 commits amostrados, 99 verificáveis, 99/99 corretamente
atribuídos por dois anotadores independentes. O Iris hoje lê uma das
quatro fontes (trailer); com esta mudança passa a ler três — o actor
login não existe offline.
Precedente interno.intent_classifier.py já registra confidence_reason por commit (iris/models/intent.py:33); esta mudança
espelha esse padrão para origem.
Cobertura de ferramentas — reativa, não por auditoria._AI_TOOL_PATTERNS
tem 9 ferramentas; o paper identificou 29. A tentação é auditar a lista
inteira — mas isso contradiz a regra adotada aqui para domínios de e-mail
("só com commit observado") e adicionaria padrões que a org talvez nunca
acione. Ferramentas novas entram quando um commit real as revela, pelo
procedimento já documentado no CLAUDE.md ("When adding a new AI tool…").
O que é concreto hoje e fica como PR pequena separada: o hook prepare_commit_msg.sh detecta por env var só $CLAUDE_CODE (:52)
enquanto seu guard reconhece 10 nomes (:103), e não há teste que mantenha
guard e tabela em paridade.
Evidência de validação (isolada: main + só esta mudança)
Python
Unitário (tests/test_origin_classifier_evidence.py, 15 testes): a
matriz da tabela acima, mais Gemini Rossi / Claudemir Silva / random-user-ai → HUMAN; dependabot[bot] / github-actions[bot] / kody-ai / copilot-autofix[bot] → BOT; trailer vence e-mail e nome;
e-mail vazio tolerado; subdomínio de vendor casa; domínios não
verificados (cursor.com, devin.ai) não classificam.
Integração (tests/test_origin_evidence_integration.py, 5 testes):
histórico sintético de 10 commits atravessa aggregate() → commit_origin_distribution == {"HUMAN": 3, "AI_ASSISTED": 4, "BOT": 3}, origin_evidence_distribution == {"trailer": 1, "author_email": 1, "author_name": 2}, ai_detection_coverage_pct == round(4/7*100, 1), campo ausente sem IA,
e acceptance/cascade/attribution_gap executam com agentes presentes.
Render (tests/test_writer_evidence_breakdown.py, 4 testes): frase
em EN e PT-BR, ordenação por contagem decrescente, posição na linha de
cobertura, ausência quando não há evidência (payloads antigos).
Suíte completa:497 pass / 0 fail (LC_ALL=C; 473 da base + 24
novos). check_analysis_chain.py: OK (22 wired, 15 opt-out — inalterado).
Platform (platform/, com npm ci --legacy-peer-deps como na CI)
npx tsc --noEmit ✓ · npx vitest run276/276 ✓ · npx eslint . 0 erros (79 warnings pré-existentes, nenhum em metrics.ts) ✓ · npx prettier --check src/types/metrics.ts ✓ · npm run build com os
placeholders de env da CI ✓ (48 páginas estáticas).
A/B end-to-end (engine base vs engine com esta mudança; mesmo
repo-alvo, runs em par no mesmo minuto — a janela --days é calculada em
UTC com granularidade de dia, e runs em momentos diferentes podem cair em
janelas diferentes; gh desabilitado para determinismo; 90 dias, churn_days=14):
metrics.json: nenhuma chave alterada ou removida; uma adicionada — origin_evidence_distribution = {"trailer": 67}.
report.md EN e PT-BR: exatamente uma linha difere — a de cobertura,
que ganha a frase de evidência.
Delta zero neste repo por construção: aqui todo commit de IA tem trailer,
e há 0 commits HUMAN em 90 dias — por isso o critério de aceite exige um
dry run em repo com agentes.
Como reproduzir
LC_ALL=C pytest tests/ -q # 497 passed
python scripts/check_analysis_chain.py # Analysis chain OKcd platform && npm ci --legacy-peer-deps && npx tsc --noEmit && npx vitest run && npx eslint .&&cd ..
# A/B determinístico: gh falso no PATH, cwd fora do repo, base e after em sequência imediata
mkdir -p /tmp/nogh &&printf'#!/bin/sh\nexit 1\n'> /tmp/nogh/gh && chmod +x /tmp/nogh/gh
cd /tmp && PATH=/tmp/nogh:$PATH iris <repo> --days 90 --churn-days 14 --no-push --out /tmp/after
Análise adversária (pós-implementação) — toda linha com disposição final
#
Ataque
Resposta
Disposição
1
Domínios de e-mail não verificados. Só anthropic.com é citado pelo paper; cursor.com, cursor.sh, devin.ai, cognition.ai seriam suposições.
Entradas erradas são inofensivas (nunca casam) mas são peso morto e passam falsa impressão de cobertura.
✅ Decidido: só anthropic.com. Cursor e Devin já são cobertos pela regra de nome (Cursor Agent, devin-ai-integration[bot]). test_unverified_vendor_domains_are_not_listed documenta o escopo; novas entradas só com commit observado.
2
copilot-autofix[bot] fica BOT, copilot-swe-agent[bot] vira AI — dois produtos Copilot com vereditos diferentes.
A lista curada _BOT_NAMES agrupa copilot-autofix em "GitHub core".
✅ Decidido: mantém BOT. O eixo é o que a conta faz, não a marca: autofix reage a alertas de code scanning (classe de dependabot/snyk-bot); o coding agent autora entrega sob pedido. Rationale no comentário de _EXPLICIT_BOT_NAME; test_explicit_bot_names_keep_curated_verdict fixa o comportamento.
3
Superfície de falso-positivo do marcador Agent. Um handle humano como gemini-agent-dev casaria.
Exige marcador e nome de ferramenta; humanos com nome de ferramenta testados como HUMAN. Risco residual real mas estreito.
✅ Mitigado; limitação documentada no comentário de _AGENT_AUTHOR_MARKER.
4
Reclassificação reescreve o histórico. Um repo pode passar de 30% para 45% de IA na versão seguinte da CLI sem mudança no código. Métricas já ingeridas não são recomputadas.
Comportamento correto, mas é descontinuidade de série. O payload carrega cli_version (iris/platform/push.py:35), a rota o armazena (route.ts:153), mas platform/lib/queries/temporal.ts não o consulta.
✅ Disposição dupla: (a) CHANGELOG no release (chain); (b) follow-up condicional — ao mesclar, abrir [DEBT] para anotar deltas que cruzam cli_version em platform/lib/queries/temporal.ts, com os números reais da primeira run ingerida. Não se abre antes: seria issue especulativa dormindo até o merge. Esta issue não depende dela para estar correta; ela evita o falso alarme visual.
5
Não testado num repo real com agentes. O A/B aqui tem delta zero por construção.
Coberto por teste de integração com commits sintéticos; não substitui campo.
✅ Critério de aceite (seção acima): dry run em ≥ 1 repo da org com commits de agente antes do merge, com distribuições anexadas à PR.
6
Jules e outros agentes ficam BOT. A regra 3 exige ferramenta em _AI_TOOL_PATTERNS; google-labs-jules[bot] não está lá.
Lacuna de cobertura, não de mecanismo — o mecanismo promove qualquer agente cuja ferramenta esteja na tabela. Testado e documentado.
✅ Decidido: cobertura é reativa. Ferramentas entram em _AI_TOOL_PATTERNS quando um commit observado as revela — a mesma regra adotada nesta issue para _AI_EMAIL_DOMAINS. Auditar uma lista de 29 ferramentas de paper adicionaria regex que a org talvez nunca acione. O que sobra de concreto (hook detecta só $CLAUDE_CODE; paridade hook↔tabela) vira PR pequena separada (ver "Ao mesclar").
7
Commits vindos da API do GitHub (path de PR) podem não ter e-mail.
author_email default ""; a regra 2 exige @ e não casa. A regra 3 funciona com o login (copilot-swe-agent[bot] é o login da GitHub App).
✅ Mitigado.
8
Custo.detect_tool_evidence roda até 3× por commit.
Regex em strings curtas; A/B mediu 4,6 s antes e depois.
✅ Mitigado (medido).
9
A frase de evidência no relatório é ruído quando há uma só fonte ("67 via trailer").
Verdadeiro para repos 100% trailer — e é exatamente a informação: toda a visibilidade depende do hook. Uma frase curta na linha já existente, sem seção nova.
✅ Decidido: manter; custo de uma frase, informação operacional real.
Fora do escopo
Nome de autor sem marcador de agente (falso-positivo medido: Claude Dupont).
Detecção por env var no hook e teste de paridade hook↔tabela → PR pequena separada (ver "Ao mesclar").
Anotação de cli_version na detecção temporal do dashboard → follow-up condicional (ver "Ao mesclar").
Recomputar métricas já ingeridas.
Novo view na platform (adiado por desenho; ver chain).
Issues relacionadas (verificado contra as 63 issues do repo, abertas e fechadas — sem duplicata)
[METRIC] Origin classifier: attribution by evidence source (email domain, agent author markers)
Referências
arquivo:linhaapontam paramain @ da16235(antes destamudança). Esta issue é independente: aplica, testa e valida sozinha sobre
main. Toda preocupação levantada na análise adversária tem disposiçãofinal — decidida, mitigada, transformada em critério de aceite desta issue,
convertida em condição ("ao mesclar…") ou desmembrada em PR separada.
Hipótese
Essa métrica nos ajuda a entender como IA muda software delivery porque
toda comparação IA × humano do Iris depende de classificar corretamente
quem escreveu cada commit — e a forma dominante de autoria por IA está
mudando de assistência (pessoa commita, IA co-assina) para agência (o
agente commita como autor, sem trailer). Se esses commits entram na baseline
humana, a diferença IA × humano encolhe artificialmente; se entram em BOT,
os commits mais "IA" de todos somem de
durability_by_origin,fix_target_by_origin,cascade_rate_by_origineacceptance_by_origin,que iteram só
HUMANeAI_ASSISTED.O registro de qual evidência identificou cada commit responde a uma
pergunta operacional que hoje não tem resposta: quanto da nossa visibilidade
de IA depende de o hook
prepare-commit-msgestar instalado?Definição
Comportamento atual (o problema)
detect_tool()lê apenascommit.attribution_trailers(
iris/analysis/origin_classifier.py:132-136). O campoCommit.author_emailexiste (
iris/models/commit.py:30) e é preenchido pelogit_reader(
iris/ingestion/git_reader.py:243), mas nunca é consultado. Teste sintéticono classificador real,
main @ da16235:Claude, e-mail[email protected]Cursor Agentcopilot-swe-agent[bot]devin-ai-integration[bot]google-labs-jules[bot]_AI_TOOL_PATTERNS)Claude Dupont, e-mail[email protected]O padrão da primeira linha é exatamente o da Fig. 1 do paper (repositório
claudes-c-compiler: 3.957 commits com Claude como autor, ~500K linhasem semanas).
Comportamento proposto
Ordem de avaliação em
classify_origin()— primeiro match vence:Co-authored-by/Assisted-by/Made-withnomeia umaferramenta de
_AI_TOOL_PATTERNS→AI_ASSISTED, evidênciatrailer.(Inalterado.)
_AI_EMAIL_DOMAINS(match pordomínio ou subdomínio, nunca por padrão de texto) →
AI_ASSISTED,evidência
author_email. A lista contém apenasanthropic.com— oúnico domínio citado pelo paper; entradas novas só com commit observado.
[bot]ou a palavraAgent, delimitada por não-alfanuméricos) e nomeia uma ferramenta de_AI_TOOL_PATTERNSe não está em_BOT_NAMES→AI_ASSISTED,evidência
author_name._BOT_AUTHOR_PATTERNScasa o nome →BOT. (Inalterado — ohuman_review_coverage.py:43,175importa esse regex diretamente e não éafetado.)
HUMAN.Novas APIs em
origin_classifier.py:detect_tool_evidence(commit) -> tuple[str, str] | None→(tool, evidence),com
evidence ∈ {"trailer", "author_email", "author_name"}.detect_tool(commit)passa a delegar paradetect_tool_evidence(APIpública preservada).
EVIDENCE_TRAILER,EVIDENCE_AUTHOR_EMAIL,EVIDENCE_AUTHOR_NAME.Novo campo em
ReportMetrics(iris/models/metrics.py, apóscommit_origin_distribution):Populado em
iris/metrics/aggregator.pylogo apósai_detection_coverage_pct,iterando
origin_classifiede contando a evidência dosAI_ASSISTED.Emitido automaticamente por
ReportMetrics.to_dict()(asdict). Aceitopela rota
/api/ingestsem mudança de contrato: o schema zod usa.passthrough()(platform/src/app/api/ingest/route.ts:45) e o OpenAPIdeclara
additionalProperties: true(platform/openapi.yaml:489); o payloadé persistido em JSONB (
platform/supabase/migrations/008_iris_tables.sql:42).Render no relatório (
iris/reports/writer.py, bloco de cobertura de IA,:262-272): a frase de evidência é anexada à linha de cobertura existente,ordenada por contagem decrescente — chaves i18n
ai_evidence_breakdown,evidence_trailer,evidence_author_email,evidence_author_nameem EN ePT-BR:
Tipo TS (
platform/src/types/metrics.ts, bloco// Origin):origin_evidence_distribution?: Partial<Record<"trailer" | "author_email" | "author_name", number>>.Efeitos downstream (todos intencionais)
aggregator.py:100-110ai_detection_coverage_pctsobe (numerador e denominador não-bot mudam)analysis/acceptance_rate.py:81-86BOT; agentes passam a entrar nas taxas de aceitação, com ferramentaanalysis/cascade_detector.py:128-134BOTnão é gatilho; agentes passam a ser gatilhos válidosanalysis/attribution_gap.py:71HUMAN; commitsClaude/Cursor Agentdeixam de ser "gap" porque estão atribuídosbuild_tool_map()→durability_by_tool,cascade_rate_by_tool,revert_by_toolanalysis/human_review_coverage.py_BOT_AUTHOR_PATTERNSem autores de review)Fonte do sinal
múltiplas fontes — git log (nome e e-mail do autor) + co-author / trailers
do hook. Todas offline; nenhuma chamada de rede nova.
Risco de ranqueamento individual
Baixo e não-novo. A classificação é por classe de origem (HUMAN /
AI_ASSISTED / BOT), não por pessoa;
origin_evidence_distributioné umagregado por repositório com três chaves, sem nomes. A regra 3 só se aplica a
contas de agente (marcador
[bot]/Agent), nunca a pessoas — e a regra2 casa por domínio de e-mail de vendor, o que exclui pessoas por
construção (
[email protected]continua humano). Nenhum campo novocarrega identidade.
Chain checklist (releasa completa)
iris/analysis/origin_classifier.py) —detect_tool_evidence,_AI_EMAIL_DOMAINS,_AGENT_AUTHOR_MARKER,_EXPLICIT_BOT_NAMEiris/metrics/aggregator.py) — populaorigin_evidence_distributioniris/models/metrics.py) — campo novo,Nonepor padrãoiris/reports/writer.py) — frase de evidência na linha de cobertura; chaves i18n EN + PT-BR; testado emtest_writer_evidence_breakdown.pyiris/reports/narrative.py) — decidido: não se aplica. A distribuição é descritiva; não há threshold natural que separe "bom" de "ruim" (um repo 100% trailer e um repo 100% agente são ambos legítimos). Um finding aqui seria opinião, não achado.platform/src/types/metrics.ts) — campo opcional adicionado;tsc, vitest, eslint, prettier enext buildverdesCLAUDE.md(Design Philosophy): "A new UI view must be justified by a metric that already tells a useful story in the engine." Nenhum repo real ainda mostrou uma distribuição com mais de uma fonte de evidência; quando o dry run do critério de aceite mostrar uma, o painel de cobertura de IA é o lugar natural (sem view nova).CLAUDE.md, Release Checklist, item 4), não pré-requisito de merge. Deve registrar a mudança de semântica (ver adversária, ataque 4).Critérios de aceite (antes do merge)
commits autorados por agente (Copilot coding agent, Devin, Cursor
background agent ou Claude autor). Anexar à PR o
commit_origin_distributione oorigin_evidence_distributionantes edepois. Aqui o delta é zero por construção (todo commit de IA tem
trailer) — a cobertura desse cenário é por teste de integração, e um
dry run real é o que confirma o comportamento em campo.
clieplatform).Ao mesclar
Dois itens nascem depois do merge, não antes — abri-los agora seria
criar issues especulativas:
[DEBT]de anotação porcli_versionemplatform/lib/queries/temporal.ts: a reclassificação mudacommit_origin_distributioneai_detection_coverage_pctpara o mesmohistórico, e a detecção período-a-período do dashboard não consulta a
versão da CLI (o payload carrega
cli_version,iris/platform/push.py:35;a rota persiste,
route.ts:153). Abrir com a descontinuidade realobservada na primeira run ingerida após o release.
prepare_commit_msg.shpassa a detectar por envvar as ferramentas que a org usa além do Claude Code, e ganha um teste de
paridade entre o guard do hook e
_AI_TOOL_PATTERNS.Notas / prior art
Paper — Y. Liu, R. Widyasari, Y. Zhao, I. C. Irsan, J. Chen, D. Lo,
Debt Behind the AI Boom: A Large-Scale Empirical Study of AI-Generated Code
in the Wild, arXiv:2603.28592v2 [cs.SE], 2026. §III-A "AI Attribution
Rules", p.3: quatro fontes de evidência em metadata do Git — (1) actor
login (ex.
copilot-swe-agent[bot]), (2) author email (ex.[email protected]), (3) author name (ex.Cursor Agent), (4) trailerCo-authored-by. 29 ferramentas deixaram rastros identificáveis. §IV-D,p.6: 100 commits amostrados, 99 verificáveis, 99/99 corretamente
atribuídos por dois anotadores independentes. O Iris hoje lê uma das
quatro fontes (trailer); com esta mudança passa a ler três — o actor
login não existe offline.
Precedente interno.
intent_classifier.pyjá registraconfidence_reasonpor commit (iris/models/intent.py:33); esta mudançaespelha esse padrão para origem.
Cobertura de ferramentas — reativa, não por auditoria.
_AI_TOOL_PATTERNStem 9 ferramentas; o paper identificou 29. A tentação é auditar a lista
inteira — mas isso contradiz a regra adotada aqui para domínios de e-mail
("só com commit observado") e adicionaria padrões que a org talvez nunca
acione. Ferramentas novas entram quando um commit real as revela, pelo
procedimento já documentado no
CLAUDE.md("When adding a new AI tool…").O que é concreto hoje e fica como PR pequena separada: o hook
prepare_commit_msg.shdetecta por env var só$CLAUDE_CODE(:52)enquanto seu guard reconhece 10 nomes (
:103), e não há teste que mantenhaguard e tabela em paridade.
Evidência de validação (isolada:
main+ só esta mudança)Python
tests/test_origin_classifier_evidence.py, 15 testes): amatriz da tabela acima, mais
Gemini Rossi/Claudemir Silva/random-user-ai→ HUMAN;dependabot[bot]/github-actions[bot]/kody-ai/copilot-autofix[bot]→ BOT; trailer vence e-mail e nome;e-mail vazio tolerado; subdomínio de vendor casa; domínios não
verificados (
cursor.com,devin.ai) não classificam.tests/test_origin_evidence_integration.py, 5 testes):histórico sintético de 10 commits atravessa
aggregate()→commit_origin_distribution == {"HUMAN": 3, "AI_ASSISTED": 4, "BOT": 3},origin_evidence_distribution == {"trailer": 1, "author_email": 1, "author_name": 2},ai_detection_coverage_pct == round(4/7*100, 1), campo ausente sem IA,e
acceptance/cascade/attribution_gapexecutam com agentes presentes.tests/test_writer_evidence_breakdown.py, 4 testes): fraseem EN e PT-BR, ordenação por contagem decrescente, posição na linha de
cobertura, ausência quando não há evidência (payloads antigos).
LC_ALL=C; 473 da base + 24novos).
check_analysis_chain.py: OK (22 wired, 15 opt-out — inalterado).Platform (
platform/, comnpm ci --legacy-peer-depscomo na CI)npx tsc --noEmit✓ ·npx vitest run276/276 ✓ ·npx eslint .0 erros (79 warnings pré-existentes, nenhum em
metrics.ts) ✓ ·npx prettier --check src/types/metrics.ts✓ ·npm run buildcom osplaceholders de env da CI ✓ (48 páginas estáticas).
A/B end-to-end (engine base vs engine com esta mudança; mesmo
repo-alvo, runs em par no mesmo minuto — a janela
--daysé calculada emUTC com granularidade de dia, e runs em momentos diferentes podem cair em
janelas diferentes;
ghdesabilitado para determinismo; 90 dias,churn_days=14):metrics.json: nenhuma chave alterada ou removida; uma adicionada —origin_evidence_distribution = {"trailer": 67}.report.mdEN e PT-BR: exatamente uma linha difere — a de cobertura,que ganha a frase de evidência.
e há 0 commits HUMAN em 90 dias — por isso o critério de aceite exige um
dry run em repo com agentes.
Como reproduzir
Análise adversária (pós-implementação) — toda linha com disposição final
anthropic.comé citado pelo paper;cursor.com,cursor.sh,devin.ai,cognition.aiseriam suposições.anthropic.com. Cursor e Devin já são cobertos pela regra de nome (Cursor Agent,devin-ai-integration[bot]).test_unverified_vendor_domains_are_not_listeddocumenta o escopo; novas entradas só com commit observado.copilot-autofix[bot]fica BOT,copilot-swe-agent[bot]vira AI — dois produtos Copilot com vereditos diferentes._BOT_NAMESagrupacopilot-autofixem "GitHub core"._EXPLICIT_BOT_NAME;test_explicit_bot_names_keep_curated_verdictfixa o comportamento.Agent. Um handle humano comogemini-agent-devcasaria._AGENT_AUTHOR_MARKER.cli_version(iris/platform/push.py:35), a rota o armazena (route.ts:153), masplatform/lib/queries/temporal.tsnão o consulta.[DEBT]para anotar deltas que cruzamcli_versionemplatform/lib/queries/temporal.ts, com os números reais da primeira run ingerida. Não se abre antes: seria issue especulativa dormindo até o merge. Esta issue não depende dela para estar correta; ela evita o falso alarme visual._AI_TOOL_PATTERNS;google-labs-jules[bot]não está lá._AI_TOOL_PATTERNSquando um commit observado as revela — a mesma regra adotada nesta issue para_AI_EMAIL_DOMAINS. Auditar uma lista de 29 ferramentas de paper adicionaria regex que a org talvez nunca acione. O que sobra de concreto (hook detecta só$CLAUDE_CODE; paridade hook↔tabela) vira PR pequena separada (ver "Ao mesclar").author_emaildefault""; a regra 2 exige@e não casa. A regra 3 funciona com o login (copilot-swe-agent[bot]é o login da GitHub App).detect_tool_evidenceroda até 3× por commit.Fora do escopo
Claude Dupont).cli_versionna detecção temporal do dashboard → follow-up condicional (ver "Ao mesclar").Issues relacionadas (verificado contra as 63 issues do repo, abertas e fechadas — sem duplicata)
Assisted-byeMade-with. É a predecessora direta: corrigiu a leitura dos três trailers e adicionou Devin à tabela. Esta issue fecha a lacuna seguinte — a evidência que não está em trailer nenhum (e-mail e nome do autor). O mecanismo de [BUG] Origin classifier ignora trailers Assisted-by e Made-with (commits de IA contados como HUMAN) #142 (trailer) fica intacto e continua sendo a regra 1.human_review_coverage_pctusa_BOT_AUTHOR_PATTERNSem autores de review; esta issue não altera esse regex (ver efeitos downstream).Referências
main @ da16235):iris/analysis/origin_classifier.py:39-59(_AI_TOOL_PATTERNS),:63-92(_BOT_NAMES),:98-101(_BOT_AUTHOR_PATTERNS),:104-136(classify_origin,detect_tool);iris/models/commit.py:28-35;iris/ingestion/git_reader.py:40-43,236-246;iris/metrics/aggregator.py:91-125;iris/reports/writer.py:262-272;iris/i18n.py:599-612,1480-1493;iris/analysis/human_review_coverage.py:43,175;platform/src/types/metrics.ts:198-202;platform/src/app/api/ingest/route.ts:45,153;platform/openapi.yaml:489.CLAUDE.md→ "When adding a new AI tool…", Absolute Rule build(deps): Bump pyjwt from 2.8.0 to 2.12.0 in /platform/scripts #5, Design Philosophy, Release Checklist;docs/guides/ai-attribution-policy.md;docs/METRICS.md§4.