Skip to content

[METRIC] human_review_coverage_pct — fração de PRs merged com revisão humana real #35

@trentas

Description

@trentas

Hipótese

Essa métrica nos ajuda a entender como IA muda software delivery porque o pr_single_pass_rate atual mistura dois mundos opostos no mesmo número: "humano revisou e aprovou direto" e "ninguém humano olhou, bot aprovou ou o autor fez merge sem ninguém ver". À medida que ferramentas de AI code review (kody.ai, Copilot Review, CodeRabbit, etc.) viram default em times de produto, a fração de PRs que recebem revisão humana real cai — sem isso aparecer em nenhum dashboard hoje. Quem está chamando "single-pass de 90%" de saudável pode estar olhando para "10% revisado por humano, 90% direto pro main com AI checking apenas".

Concretamente: o caso recém-descoberto no RocketBus/clickbus-platform-general-microservices motivou esse pedido — pr_single_pass_rate = 0.9 parece ótimo, mas Flow Efficiency revelou que a maioria desses PRs vai do open ao merge sem evento humano, com kody-ai cobrindo a revisão.

Definição

Para cada PR merged na janela:

  • had_human_review = True se algum review no PR foi submetido por autor que não casa com _BOT_AUTHOR_PATTERNS em origin_classifier.py
  • had_human_approval = True se algum review com state == "APPROVED" foi submetido por autor não-bot

Agregados emitidos (sempre opcionais; None quando não houver PRs merged na janela):

  • human_review_coverage_pct — fração de PRs merged com had_human_review == True (0.0–1.0)
  • human_approval_coverage_pct — fração de PRs merged com had_human_approval == True

Quebras secundárias (omitir segmento abaixo de min_sample = 10 PRs, alinhado com Flow Efficiency):

  • human_review_coverage_by_intentRecord<ChangeIntent, float>
  • human_review_coverage_by_origin_of_prRecord<CommitOrigin, float> (origin do PR pela regra ≥50% AI commits, igual Flow Efficiency)

Edge cases:

  • PR sem reviews — had_human_review = False, conta no denominador (não inflar a métrica filtrando esses out).
  • PR com apenas reviews de bot — had_human_review = False, igual.
  • Bot detection reusa exatamente o _BOT_AUTHOR_PATTERNS regex (e a lista _BOT_NAMES) do origin_classifier.py — uma só fonte de verdade entre origin / flow_efficiency / human_review_coverage.

Fonte do sinal

múltiplas fontes

(PullRequest.reviews[].author via gh CLI; bot detection via iris/analysis/origin_classifier.py)

Risco de ranqueamento individual

Baixo, por construção, se respeitarmos duas regras:

  1. Não expor por PR individual. Como Flow Efficiency, calcular had_human_review por PR é intermediário; só o agregado window-level sai no schema/UI.
  2. Não atribuir a reviewers específicos. "Cobertura de review humano" é propriedade do sistema, não de quem revisou ou deveria ter revisado. Code review da PR de implementação checa que nenhum endpoint ou output liga had_human_review a um nome de pessoa.

O numerador é uma contagem booleana por PR; não tem espaço estrutural pra apontar "Fulano não revisou".

Chain checklist (releasa completa)

  • iris/analysis/human_review_coverage.py — funções compute_coverage(pr) -> tuple[bool, bool] (intermediário) e analyze_human_review_coverage(prs) -> HumanReviewCoverageResult (agregados only)
  • iris/metrics/aggregator.py — wiring + emissão dos 4 campos
  • iris/models/metrics.py — schemas (human_review_coverage_pct, human_approval_coverage_pct, e os 2 by_*)
  • iris/reports/writer.py — saída em JSON (auto via to_dict)
  • iris/reports/narrative.py — finding threshold-based. Sugestão: "Apenas X% dos PRs merged tiveram review humano" quando human_review_coverage_pct < 0.5 (hipótese a calibrar). Outro: "Cobertura de review humano caiu em PRs AI_ASSISTED vs HUMAN" quando o gap by_origin_of_pr for >= 20 pp.
  • iris/i18n.py — strings dos findings em en e pt-br
  • platform/src/types/metrics.ts — TS types
  • Platform UI — colocação aberta. Sugestão inicial: pequeno card complementar ao pr_single_pass_rate na seção de PR lifecycle (ou junto com Flow Efficiency, pois a interpretação anda junto: "Flow Efficiency baixo + Human Review Coverage baixo = pipeline majoritariamente AI-só").
  • docs/METRICS.md — entrada nova; explicar a diferença vs pr_single_pass_rate e como interpretar os dois juntos
  • Tests — coverage por intent/origin respeita min_sample; bot-only PRs contam como False; mistos contam como True

Notas / prior art

Prior art: Não conheço métrica nomeada exatamente assim em produtos do mercado. LinearB e Pluralsight Flow expõem "review rate" e "approval rate", mas misturando humanos e bots indiscriminadamente — mesma armadilha que pr_single_pass_rate. A diferenciação humano/bot é viável agora porque a detecção de bots em origin_classifier já é robusta o suficiente (extendida ao longo de #34 com kody/clickbus-pai).

Relação com Flow Efficiency: complementar. Flow Efficiency responde "do tempo elapsed, quanto foi ativo?". Esta responde "quantos PRs tiveram humano olhando?". Os dois juntos contam a história completa:

  • flow_efficiency_median alta + human_review_coverage_pct alta → fluxo saudável e bem revisado
  • flow_efficiency_median baixa + human_review_coverage_pct baixa → fluxo majoritariamente AI-só, time não está olhando
  • flow_efficiency_median baixa + human_review_coverage_pct alta → revisores humanos sobrecarregados, fila longa entre eventos
  • flow_efficiency_median alta + human_review_coverage_pct baixa → raro; talvez PRs pequenos auto-aprovados rapidinho

Estimativa de tamanho: módulo pequeno (~80-120 linhas), chain todo somando ~200-300 linhas incluindo testes e UI. Sem pré-requisito de modelo — usa PullRequest.reviews que já existe e _BOT_AUTHOR_PATTERNS que já existe.

Decisões em aberto (resolver no PR de implementação):

  1. Distinguir human_review_coverage (qualquer review humano) vs human_approval_coverage (review com state APPROVED) vale a pena, ou um deles é suficiente? Hipótese: ambos, porque "humano comentou mas não aprovou" é caso comum em times com pressa.
  2. Threshold pra finding "low coverage" — 0.5 é palpite. Calibrar com 3-5 repos.
  3. UI: card separado vs anotação dentro do Flow Efficiency card. Decisão de design.

Metadata

Metadata

Assignees

No one assigned

    Labels

    type: metricNova métrica ou alteração de métrica existente

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions