Automated code review skill operating in two modes: (1) Standalone — find PR, run review, list BLOCKERs and ask user for approval before fixing; (2) Embedded — called by task-implementation-flow at Phase 8, auto-fixes all BLOCKERs without asking. Uses 3 review agents + 1 tooling runner (diff-only, token-efficient). Review is issue-scoped and validates only dimensions justified by the Jira task plus the PR diff. Supports all Jira formats (key, URL) and can be triggered standalone or embedded.
O invocador é o ORQUESTRADOR GERAL. Ele nunca executa tools diretamente (sem Jira, sem git, sem bash, sem gh). Ele apenas spawna um Orquestrador de Fase para cada fase. Cada orquestrador pode spawnar subagentes conforme necessário.
Quando o orquestrador for invocado pelo usuário, ele opera em modo standalone;
quando invocado pelo task-implementation-flow, opera em modo embedded (sem interação, auto-fix).
Token efficiency: 3 review agents (não 8), cada um com escopo focado, operando sobre diffs (não arquivos completos). Tooling Runner separado executa comandos sem processar output em tokens.
Regra de escopo obrigatória: o review só pode validar dimensões que pertençam ao contexto combinado de issue Jira + diff real do PR. Se docs, swagger, coverage, testes ou qualquer outra dimensão não fizerem parte desse contexto, a dimensão deve ser marcada como SKIPPED / fora do escopo — nunca como aprovada, reprovada ou pendente.
Review centrado nos Acceptance Criteria: O review valida se a implementação atende os ACs da issue e segue os padrões do projeto. NÃO adicione BLOCKERs ou WARNINGs fora dos ACs. NÃO invente regras novas. A pergunta-guia é: "O código implementa os ACs seguindo os padrões existentes?"
/task-code-review-flow CORE-28
/task-code-review-flow https://gmasson.atlassian.net/browse/CORE-28
Fluxo:
Importante: Commits de fix sempre vão para o branch do PR, nunca para prod. Merge para prod ocorre APENAS via gh pr merge.
O task-implementation-flow Fase 8 (Code Review Orchestrator) deve executar esta skill em modo EMBEDDED:
IMPORTANTE: O code review DEVE operar dentro da worktree para que fixes sejam commitados no mesmo PR.
Após gh pr create retornar sucesso:
Spawne um Code Review Agent () com contexto:
{
mode: "embedded",
issueKey: "${issueKey}",
prNumber: <número do PR>,
branchName: "${branchName}",
worktreePath: "${worktreePath}", // <-- CRÍTICO: usar worktree, não repo principal
repoPath: "${repoPath}",
baseBranch: "prod",
issueData: <dados da issue>
}
O Code Review Agent segue task-implementation-flow em modo embedded:
- Executa TODAS as fases de review DENTRO da worktree
- Encontra BLOCKERs? → spawna Fix Agents DENTRO da worktree
- Re-valida, commit+push para branch do PR (não para prod!)
- NÃO pergunta ao usuário
- SE 0 BLOCKERs não resolvidos:
→ Merge PR para ${baseBranch} (gh pr merge --squash --delete-branch)
→ Transiciona Jira → DONE
→ Adiciona comentário final no Jira
- SE há BLOCKERs pendentes:
→ NÃO faz merge
→ Transiciona Jira → Code Review
→ Adiciona comentário bloqueante
${branchName}, nunca para ${baseBranch}ORQUESTRADOR GERAL
│ ← input: ISSUE-KEY (standalone) | context JSON (embedded)
│
├─ spawn → [FASE 1] Discovery Orchestrator
│ └─ Pode spawnar subagentes conforme necessidade
│ └─ RETORNA: issueData + prData + diff
│
├─ spawn → [FASE 2] Parallel Review Orchestrator
│ ├─ A. Code Quality Agent (DRY, SOLID, architecture, inline styles)
│ ├─ B. Security & Validation Agent (OWASP, tenant, decorators, zod)
│ ├─ C. Docs & Tests Agent (swagger, patterns, coverage gates, ACs)
│ └─ D. Tooling Runner (lint, typecheck, test, build)
│ └─ Pode spawnar subagentes conforme necessidade
│ └─ RETORNA: reviewResults
│
├─ spawn → [FASE 3] Synthesis Orchestrator
│ └─ Pode spawnar subagentes conforme necessidade
│ └─ SE standalone com BLOCKERs: pergunta ao usuário
│ └─ SE embedded: procede automaticamente
│ └─ RETORNA: findings + severity
│
├─ spawn → [FASE 4] Fix Orchestrator (somente se há BLOCKERs e aprovação)
│ ├─ Backend Fix Agent + Frontend Fix Agent (paralelo)
│ ├─ Re-valida (lint+typecheck+test)
│ └─ Commit+push
│ └─ Pode spawnar subagentes conforme necessidade
│ └─ RETORNA: fixesApplied + revalidationResult
│
└─ spawn → [FASE 5] Delivery Orchestrator
├─ PR comment + PR body update + Jira comment + Jira transition
├─ (transition apenas se 0 BLOCKERs restantes)
└─ Pode spawnar subagentes conforme necessidade
└─ RETORNA: prCommented + jiraTransitioned + prMerged
Agent({
description: 'Discover Jira issue and PR for ${issueKey}',
prompt: `
Você é o agente de descoberta do code review ${issueKey}.
Parse da entrada:
- Se contiver "atlassian.net/browse/", extrair a chave após "/browse/"
- Se for padrão "PROJ-123", usar diretamente
- Caso contrário, reportar erro de parse
## 1. Fetch Jira Issue
Use atlassian-rovo-getJiraIssue:
cloudId: "ed98e5b7-9db4-4052-9766-68a5c3590ccc"
issueIdOrKey: "${issueKey}"
responseContentFormat: "markdown"
Extraia e retorne estruturado:
{
key: string,
summary: string,
description: string,
acceptanceCriteria: ["AC1", "AC2", ...],
status: string,
assignee: string,
components: ["module1", "module2"]
}
## 2. Find GitHub PR
Estratégias (nesta ordem):
a) gh pr list --search "${issueKey}" --json number,title,headRefName,baseRefName,state,url --limit 10
b) Se nenhum, buscar por padrão de branch: gh pr list --search "head:${issueKey.toLowerCase()}" --limit 5
c) Se ainda nenhum, verificar ramos locais: git branch -r | grep -i "${issueKey.toLowerCase()}"
Se encontrar múltiplas PRs, escolha a mais recente (maior number) com state="open".
Se nenhuma PR encontrada: retorne { prFound: false } e pare.
## 3. Fetch PR Details
gh pr view <prNumber> --json number,title,body,headRefName,baseRefName,additions,deletions,changedFiles,url,createdAt
Use o campo baseRefName retornado pelo PR como baseBranch para todos os passos seguintes.
## 4. Fetch e Chunk do Diff do PR
Busque o diff COMPLETO do PR usando o endpoint do GitHub (não git local):
gh pr diff <prNumber>
Divida o output em chunks por categoria de arquivo (use os cabeçalhos "diff --git a/..." para identificar arquivos):
- backend: arquivos *.ts excluindo *.spec.ts e *.test.ts
- frontend: arquivos *.tsx, *.css, *.scss, *.module.*
- tests: arquivos *.spec.ts, *.test.ts, *.spec.tsx, *.test.tsx
- docs: arquivos *.md e caminhos que comecem com .specs/ ou docs/
- config: arquivos *.json, *.yaml, *.mjs, *.cjs, turbo.json, nest-cli.json
Preserve o cabeçalho "diff --git" + "--- a/ +++ b/" + hunks "@@ ... @@" de cada arquivo no seu chunk.
Se um chunk ficar vazio (nenhum arquivo da categoria), use string vazia "".
Registre a lista de todos os arquivos alterados (só os paths, sem conteúdo do diff).
## 5. Derivar issueScope e diffAnalysis
Com base na issue (summary + ACs) e nos arquivos do diff, derive:
### issueScope — O que esta issue pede
Analise o summary e os ACs para determinar:
{
"description": "frase curta do que a issue pede (ex: 'Alterar cor do botão de submit')",
"requiresNewApiEndpoints": boolean, // issue cria ou modifica endpoints HTTP?
"requiresNewDomainLogic": boolean, // issue adiciona regras de negócio em domain/application?
"requiresDocUpdate": boolean, // issue introduz padrão novo de arquitetura ou convenção?
"requiresTestsForNewLogic": boolean // há nova lógica que genuinamente precisa de testes?
}
### diffAnalysis — O que o diff realmente contém
Analise os arquivos alterados para detectar:
{
"hasControllerChanges": boolean, // diff.backend contém arquivos *controller*.ts ou *controller.spec.ts
"hasDomainLogicChanges": boolean, // diff.backend contém arquivos em /domain/ ou /application/
"hasNewDomainFiles": boolean, // diff.backend adiciona arquivos NOVOS ("+++ b/") em domain/ ou application/
"hasDTOChanges": boolean, // diff.backend contém arquivos *dto*.ts ou *request*.ts ou *response*.ts
"hasFrontendChanges": boolean, // diffChunks.frontend não está vazio
"hasTestChanges": boolean, // diffChunks.tests não está vazio
"hasDocChanges": boolean, // diffChunks.docs não está vazio
"hasConfigChanges": boolean // diffChunks.config não está vazio
}
## 6. Adicionar Comentário Jira
Use atlassian-rovo-addCommentToJiraIssue:
cloudId: "ed98e5b7-9db4-4052-9766-68a5c3590ccc"
issueIdOrKey: "${issueKey}"
comment: "🔍 Code review iniciado por Claude Code.\\nPR: <prUrl>\\nBranch: <headRefName> → <baseRefName>\\nEscopo: <issueScope.description>\\nStatus: Executando análise..."
## Retorne JSON:
{
"issueData": { key, summary, acceptanceCriteria, status, assignee, technicalNotes },
"prData": {
"prFound": true,
"number": <prNumber>,
"title": string,
"url": string,
"headRefName": string,
"baseRefName": string,
"additions": number,
"deletions": number,
"changedFiles": number
},
"diffChunks": {
"backend": string,
"frontend": string,
"tests": string,
"docs": string,
"config": string,
"changedFiles": ["path/to/file.ts", ...]
},
"issueScope": {
"description": string,
"requiresNewApiEndpoints": boolean,
"requiresNewDomainLogic": boolean,
"requiresDocUpdate": boolean,
"requiresTestsForNewLogic": boolean
},
"diffAnalysis": {
"hasControllerChanges": boolean,
"hasDomainLogicChanges": boolean,
"hasNewDomainFiles": boolean,
"hasDTOChanges": boolean,
"hasFrontendChanges": boolean,
"hasTestChanges": boolean,
"hasDocChanges": boolean,
"hasConfigChanges": boolean
},
"jiraCommentAdded": boolean,
"error": null | "message"
}
`,
subagent_type: 'general',
});
Error handling: Se prFound: false ou issue não encontrada, orquestrador reporta e para.
Agent({
description: 'Code Quality review for ${issueKey} (diff-only)',
prompt: `
Você é o agente de Code Quality do PR ${prNumber} (${issueKey}).
Escopo da issue: "${issueScope.description}"
Contexto recebido: summary="${summary}" | technicalNotes="${technicalNotes}"
## Princípio de Escopo
Valide APENAS o que está nos ACs da issue e nos padrões do projeto (AGENTS.md).
NÃO sinalize itens fora do escopo dos ACs como BLOCKER.
Se um padrão do projeto foi violado MAS está fora do escopo dos ACs, classifique como
SUGGESTION no máximo — nunca BLOCKER.
Pergunte a si mesmo antes de cada finding: "Este item impede os ACs de serem atendidos?"
Se não → no máximo SUGGESTION.
⚠️ REGRA DE ESCOPO — LEIA ANTES DE ANALISAR:
Analise APENAS o código que está nos diffs abaixo. Não sinalize ausências nem violações
em código que não foi modificado por este PR. Se o diff de uma seção estiver vazio,
pule completamente os checklist items daquela seção.
Analise APENAS o diff abaixo (já pré-filtrado — não execute git nem leia arquivos adicionais):
=== BACKEND DIFF (*.ts excl. testes) ===
${diffChunks.backend || '(vazio — nenhum arquivo backend alterado)'}
=== FRONTEND DIFF (*.tsx, *.css) ===
${diffChunks.frontend || '(vazio — nenhum arquivo frontend alterado)'}
=== CONFIG DIFF (*.json, *.yaml, *.mjs) ===
${diffChunks.config || '(vazio — nenhum arquivo config alterado)'}
===
Se uma seção estiver "(vazio)", pule os itens de checklist daquela seção inteiramente.
Não invente findings em seções sem diff.
Para cada hunk alterado, analise focando em:
## 1. Architecture & DDD
BLOCKER:
- Prisma importado no domain (qualquer import de @prisma)
- Cross-module import sem port (catalog importando sales diretamente)
- Entity vs Value Object confusion
WARNING:
- Função >25 linhas sem clara responsabilidade
- Código duplicado que poderia ser extraído
## 2. Backend Patterns (NestJS)
BLOCKER:
- console.log em arquivo de produção (deve ser this.logger.log)
- Float usado para money (price: number em vez de priceInCents: number)
- Prisma fora de infrastructure/
WARNING:
- Missing @Injectable() em um service
- Falta de error handling em async operations
## 3. Frontend Components (React)
BLOCKER:
- style={{}} inline em page.tsx ou componente de feature
(deve ser extraído para componente separado — EXTREMAMENTE RARO se necessário)
WARNING:
- style={{}} em componente interno com motivo duvidoso
- Função de componente >200 linhas sem composição
## 4. Naming & Conventions
SUGGESTION:
- Entity: PascalCase (User, Order)
- Repository: IUserRepository ou UserRepository
- UseCase: PascalCase + Ação (CreateUserUseCase)
- NestJS module: PascalCase + Module (CatalogModule)
- Frontend component: PascalCase
- Hook: camelCase com "use" prefix
- Constante: SCREAMING_SNAKE_CASE
Retorne JSON com max 10 findings:
{
"dimension": "code-quality",
"findings": [
{
"severity": "BLOCKER|WARNING|SUGGESTION",
"file": "path/to/file.ts",
"line": number,
"title": "short title",
"description": "what's wrong",
"suggestion": "how to fix",
"code_snippet": "trecho problemático"
}
],
"summary": "X BLOCKERs, Y WARNINGs encontrados"
}
`,
subagent_type: 'general',
});
Agent({
description: 'Security & Validation review for ${issueKey}',
prompt: `
Você é o agente de Security & Validation do PR ${prNumber} (${issueKey}).
Escopo da issue: "${issueScope.description}"
Contexto recebido: summary="${summary}"
## Princípio de Escopo
Valide APENAS o que está nos ACs da issue e nos padrões do projeto (AGENTS.md).
NÃO sinalize itens fora do escopo dos ACs como BLOCKER.
Se um padrão do projeto foi violado MAS está fora do escopo dos ACs, classifique como
SUGGESTION no máximo — nunca BLOCKER.
Pergunte a si mesmo antes de cada finding: "Este item impede os ACs de serem atendidos?"
Se não → no máximo SUGGESTION.
⚠️ REGRA DE ESCOPO — LEIA ANTES DE ANALISAR:
Analise APENAS o código que está nos diffs abaixo.
- SE diffAnalysis.hasControllerChanges === false → pule verificações de @UseGuards e @ApiOperation
- SE diffAnalysis.hasDTOChanges === false → pule verificações de decorators de validação em DTOs
- SE diffAnalysis.hasFrontendChanges === false → pule verificações de zod schema e aria-label
- SE issueScope.requiresNewApiEndpoints === false E diffAnalysis.hasControllerChanges === false
E diffAnalysis.hasDTOChanges === false → trate swagger/decorators HTTP como SKIPPED
- Nunca sinalize ausências em arquivos que não estão no diff
- Se esta issue for somente visual/frontend e não houver diff backend relevante, não invente
findings de guards, swagger, DTOs ou validação backend
Analise APENAS o diff abaixo (já pré-filtrado — não execute git nem leia arquivos adicionais):
=== BACKEND DIFF (*.ts excl. testes) ===
${diffChunks.backend || '(vazio — nenhum arquivo backend alterado)'}
=== FRONTEND DIFF (*.tsx, *.css) ===
${diffChunks.frontend || '(vazio — nenhum arquivo frontend alterado)'}
=== CONFIG DIFF (*.json, *.yaml, *.mjs) ===
${diffChunks.config || '(vazio — nenhum arquivo config alterado)'}
===
Se uma seção estiver "(vazio)", pule os itens daquela seção.
Analise focando em:
## 1. OWASP Top 10
BLOCKER:
- Endpoint sem @UseGuards() ou equivalent (authorization missing)
- Query sem filtro de storeId quando deveria ter (tenant leak)
- Prisma $queryRaw ou $executeRaw sem parametrização segura
- Secret ou API key hardcoded
WARNING:
- DTO sem @IsString(), @IsNotEmpty() etc. (input validation incompleta)
- Zod schema ausente em form (frontend validation missing)
- PII (CPF, password, card) logado em arquivo
## 2. Decorators & Validation (NestJS)
BLOCKER:
- Novo DTO sem decorators de validação
- Missing @ApiOperation, @ApiResponse, @ApiTags em controller novo
WARNING:
- @ApiResponse status code não bate com implementação real
- DTO de request/response sem @ApiProperty em campo
## 3. Frontend Validation (React + zod)
WARNING:
- Form sem zod schema ou com schema incompleto
- Input sem label e aria-label
Retorne JSON:
{
"dimension": "security",
"findings": [...],
"summary": "X BLOCKERs encontrados"
}
`,
subagent_type: 'general',
});
Agent({
description: 'Docs & Tests validation for ${issueKey}',
prompt: `
Você é o agente de Docs & Tests do PR ${prNumber} (${issueKey}).
Escopo da issue: "${issueScope.description}"
Contexto recebido: acceptanceCriteria (lista abaixo) — não receba nem leia outros campos da issue.
## Princípio de Escopo
Valide APENAS o que está nos ACs da issue e nos padrões do projeto (AGENTS.md).
NÃO sinalize itens fora do escopo dos ACs como BLOCKER.
Se um padrão do projeto foi violado MAS está fora do escopo dos ACs, classifique como
SUGGESTION no máximo — nunca BLOCKER.
Pergunte a si mesmo antes de cada finding: "Este item impede os ACs de serem atendidos?"
Se não → no máximo SUGGESTION.
⚠️ REGRA DE ESCOPO — LEIA ANTES DE ANALISAR:
Cada checklist abaixo SÓ se aplica SE a condição de gate for verdadeira.
Não sinalize ausências em dimensões fora do escopo desta issue.
GATES:
- Verificação de test coverage → SOMENTE SE issueScope.requiresTestsForNewLogic === true
OU diffAnalysis.hasDomainLogicChanges === true
OU diffAnalysis.hasNewDomainFiles === true
(nova lógica/regra de negócio entrou de fato no escopo do PR)
- Verificação de Swagger → SOMENTE SE issueScope.requiresNewApiEndpoints === true
OU diffAnalysis.hasControllerChanges === true
OU diffAnalysis.hasDTOChanges === true
(o PR adiciona/modifica endpoints, controllers ou contratos HTTP)
- Verificação de docs consistency → SOMENTE SE diffAnalysis.hasDocChanges === true
OU issueScope.requiresDocUpdate === true
- AC validation → SEMPRE (validar os ACs da issue contra o que o diff implementa)
Se um gate for false, escreva na seção correspondente: "(skipped — fora do escopo desta issue)"
e não sinalize nenhum finding nessa dimensão.
Nunca emita BLOCKER/WARNING por falta de docs, swagger ou testes quando o gate correspondente
estiver false. Exemplo: issue de trocar botão sem diff de controller/docs/domain não deve gerar
finding de swagger, docs ou coverage.
Acceptance Criteria:
${JSON.stringify(issueData.acceptanceCriteria)}
Arquivos alterados no PR: ${diffChunks.changedFiles.join(', ')}
Analise APENAS o diff abaixo (já pré-filtrado — não execute git nem leia arquivos adicionais):
=== TESTS DIFF (*.spec.ts, *.test.ts) ===
${diffChunks.tests || '(vazio — nenhum arquivo de teste alterado)'}
=== DOCS DIFF (*.md, .specs/**, docs/**) ===
${diffChunks.docs || '(vazio — nenhum arquivo de docs alterado)'}
===
## 1. Docs Consistency Check
Verificar se o diff toca arquivos de regras:
AGENTS.md, CLAUDE.md, docs/ai/*, .specs/features/**
SE sim:
- Validar se a mudança é consistente com o código da issue
- Não há contradição? Marcar como OK.
- Há contradição? BLOCKER "Docs inconsistente com implementação"
SE não tocou docs, mas o código introduz padrões novos:
BLOCKER:
- Novo decorator customizado sem documentação
- Nova utilidade em packages/shared sem AGENTS.md reference
- Novo pattern de componente React sem docs
- Nova convenção de naming não registrada
## 2. Test Coverage Validation
Executar (já fornecido pelo Tooling Runner, mas você valida):
- Para cada arquivo novo em */domain/* ou */application/*:
- Existe arquivo .spec.ts correspondente?
- Coverage >= 90%?
- Para cada arquivo novo em */infrastructure/* ou hook frontend:
- Coverage >= 80%?
BLOCKER:
- Domain/application arquivo novo sem nenhum teste
- Coverage < 90% em domain/application
- AC NOT_SATISFIED (leia issue acceptance criteria e verifique código)
WARNING:
- Coverage < 80% em adapter/hook
- AC PARTIAL (parcialmente implementado)
## 3. Swagger Validation
Para cada endpoint novo ou modificado no diff:
- Tem @ApiOperation({ summary: "..." })?
- Tem @ApiResponse({ status: ..., type: ... })?
- Tem @ApiTags(["module"])?
Para cada DTO de request/response:
- Todos os campos têm @ApiProperty()?
- Status codes em @ApiResponse bate com a implementação?
BLOCKER:
- Endpoint novo sem decorators swagger
- DTO novo sem @ApiProperty em campos
WARNING:
- @ApiResponse status code incorreto vs. implementação
- DTO com campo sem @ApiProperty
Retorne JSON:
{
"dimension": "docs-tests",
"findings": [...],
"coverage": { domain: "X%|N/A", adapters: "Y%|N/A" },
"scopeStatus": {
"docsConsistency": "active|skipped",
"testsCoverage": "active|skipped",
"swagger": "active|skipped",
"acceptanceCriteria": "active"
},
"acValidation": [{ criterion: "AC text", satisfied: true|false }],
"summary": "..."
}
`,
subagent_type: 'general',
});
Agent({
description: 'Run tooling checks for ${issueKey}',
prompt: `
Você é o agente de tooling do code review.
Worktree: ${worktreePath}
Execute em sequência dentro da worktree:
cd ${worktreePath}
pnpm lint 2>&1
pnpm type-check 2>&1
pnpm test --coverage --reporter=json 2>&1
pnpm build 2>&1
Para tests: extraia do JSON de cobertura os valores de domain/ e adapters/.
Se algum script não existir: marque como { ok: true, skipped: true }.
Retorne:
{
"lint": { ok: boolean, errors: string[] },
"typecheck": { ok: boolean, errors: string[] },
"tests": { passed: boolean, total: number, failed: number, coverage: { domain: "X%", adapters: "Y%" } },
"build": { ok: boolean, errors: string[] }
}
`,
subagent_type: 'general',
});
Agent({
description: 'Synthesize findings and handle user interaction (standalone)',
prompt: `
Você é o agente de Síntese do code review ${issueKey} (PR #${prNumber}).
Dados recebidos (APENAS o necessário — nada além disto):
- codeQualityFindings: ${JSON.stringify(codeQualityAgent.findings)}
- securityFindings: ${JSON.stringify(securityAgent.findings)}
- docsTestsFindings: ${JSON.stringify(docsTestsAgent.findings)}
- acValidation: ${JSON.stringify(docsTestsAgent.acValidation)}
- coverage: ${JSON.stringify(docsTestsAgent.coverage)}
- docsTestsScopeStatus: ${JSON.stringify(docsTestsAgent.scopeStatus)}
- toolingResults: ${JSON.stringify(toolingResults)}
- issueKey: "${issueKey}"
- summary: "${issueData.summary}"
- acceptanceCriteria: ${JSON.stringify(issueData.acceptanceCriteria)}
- issueScope: ${JSON.stringify(issueScope)}
- diffAnalysis: ${JSON.stringify(diffAnalysis)}
- mode: "${mode}"
## 1. Agrega Findings
Compile uma lista unificada de todos os findings, ordenada por severidade:
- BLOCKER (devem ser corrigidos)
- WARNING (idealmente corrigidos)
- SUGGESTION (melhorias)
## 2. Classifica Bloqueadores por Auto-fixabilidade
PODE auto-fix:
✓ console.log → this.logger.log (simples string replace)
✓ price: number → priceInCents: number + callers óbvios
✓ Missing @IsString() em DTO (pattern claro)
✓ Padrão novo não documentado → adiciona linha em AGENTS.md
DEVE reportar (não auto-fix):
✗ Prisma no domain (requer refactor arquitetural)
✗ Testes faltando (contexto específico, não trivial)
✗ AC NOT_SATISFIED (requer implementação lógica)
✗ Tenant isolation leak (requer análise de negócio)
## 2.0. Identifica Deferred Items (SUPER raro)
Um item SÓ pode ser "deferred" se atender TODOS os critérios:
1. É um BLOCKER genuíno (não apenas WARNING ou SUGGESTION)
2. Está fora do escopo dos ACs desta issue
3. Não pode ser auto-fixado de forma determinística
4. Representa risco real se não tratado (segurança crítica, corrupção de dados, etc.)
Exemplos válidos: falha arquitetural que vaza dados entre tenants, secret hardcoded
Exemplos INVÁLIDOS: README faltando, timeout não configurado, library não instalada
Se um item passar por esses critérios, marque-o como deferredItem:
{
"title": "string",
"category": "security|architecture|docs",
"description": "string",
"suggestion": "string",
"whyDeferred": "Explique por que não pode ser auto-fixado nem está nos ACs"
}
Caso contrário, trate como blocker auto-fixável ou downgrade para SUGGESTION.
## 2.1. Monte o veredito por escopo antes de escrever o relatório
Derive status por dimensão e subdimensão:
- Code Quality: ACTIVE se houver diff backend/frontend/config; caso contrário SKIPPED
- Security: ACTIVE apenas quando houver sinais reais em diffAnalysis para controller/DTO/frontend/config;
caso contrário SKIPPED
- Docs & Tests:
- docsConsistency = docsTestsScopeStatus.docsConsistency
- testsCoverage = docsTestsScopeStatus.testsCoverage
- swagger = docsTestsScopeStatus.swagger
- acceptanceCriteria = sempre ACTIVE
Regras obrigatórias:
- Dimensão ou subdimensão SKIPPED deve aparecer como "fora do escopo", nunca como ✅
- Só mencione coverage numérico se testsCoverage === "active"
- Só mencione swagger findings se swagger === "active"
- Só mencione docs findings se docsConsistency === "active"
- Se a issue for, por exemplo, apenas frontend visual e o PR não tocar controller/docs/domain,
o relatório final deve mostrar Security e Docs/Swagger/Tests como SKIPPED
## 3. Modo Standalone — Perguntar ao Usuário
Construa relatório compacto:
🔍 Code Review — ${issueKey} | PR #${prNumber}
Status: ${finalStatus}
BLOCKERs (${blockerCount}):
${blockers
.slice(0, 5)
.map((b) => ` [${b.dimension.toUpperCase()}] ${b.title}`)
.join('\\n')}
${blockers.length > 5 ? ' ... e mais' : ''}
Warnings (${warningCount}) | Suggestions (${suggestionCount})
Coverage: ${
docsTestsScopeStatus.testsCoverage === 'active'
? `Domain ${coverageDomain}% (gate: 90%) | Adapters ${coverageAdapters}% (gate: 80%)`
: 'skipped — sem nova lógica/domain files no escopo'
}
ACs: ${acSatisfied}/${acTotal} atendidos
Tooling: Lint ${tooling.lint.ok ? '✅' : '❌'} | TypeCheck ${tooling.typecheck.ok ? '✅' : '❌'} | Tests ${tooling.tests.passed ? '✅' : '❌'} | Build ${tooling.build.ok ? '✅' : '❌'}
Deseja que eu corrija os BLOCKERs agora? [S/N]
Aguarde resposta do usuário.
## 4. Modo Embedded — Prosseguir Automaticamente
SE modo === "embedded":
- Log: "Modo embedded ativado — corrigindo BLOCKERs automaticamente"
- Prossiga para Fase 4 (Fix Agents)
## 5. Decisão Pós-Síntese
SE há blockers auto-fixáveis (e nenhum deferred):
- Fix agents corrigem → re-valida → DONE + merge
SE há deferredItems (SUPER raro — fora dos ACs + risco real):
- Cria CONCERNS issue no Jira
- Referencia na issue original
- Issue original → DONE (deferred items ficam em CONCERNS para ação manual)
SE há blockers NÃO auto-fixáveis E NÃO são deferredItems válidos:
- Revisar se realmente são BLOCKERs ou podem ser downgradados para WARNING
- Se genuíno: Code Review (aguarda resolução manual)
SE mode==="standalone" E usuário respondeu "N":
- Prossiga para Delivery Agent (sem Fix)
- Label PR como "changes-requested"
- NÃO transicione Jira
SE mode==="standalone" E usuário respondeu "S":
- Prossiga para Fix Agents (Fase 4)
SE mode==="embedded":
- Prossiga para Fix Agents (Fase 4) automaticamente
Retorne:
{
"mode": "standalone|embedded",
"userApproved": boolean | null (null se embedded),
"blockers": [{ severity, dimension, title, autoFixable }],
"dimensionStatus": {
"codeQuality": "active|skipped",
"security": "active|skipped",
"docsConsistency": "active|skipped",
"testsCoverage": "active|skipped",
"swagger": "active|skipped"
},
"warningsCount": number,
"suggestionsCount": number,
"coverageReport": { domain, adapters },
"toolingStatus": { lint, typecheck, tests, build },
"acStatus": { satisfied, total },
"proceedToFix": boolean
}
`,
subagent_type: 'general',
});
Se proceedToFix: true, spawne paralelo:
Agent({
description: "Apply backend fixes for ${issueKey}",
prompt: `
Você é o agente de fix backend do code review ${issueKey}.
Worktree: ${worktreePath}
BLOCKERs a corrigir (somente autoFixable, somente backend):
${JSON.stringify(backendBlockers.filter(b => b.autoFixable))}
Para cada blocker: leia SOMENTE o arquivo indicado em `blocker.file`, aplique o fix e salve.
Não leia arquivos que não estejam listados nos blockers acima.
Exemplos:
- console.log → this.logger.log
- price: number → priceInCents: number
- DTO field sem @IsString() → adicione decorator
Retorne lista de arquivos corrigidos com resultado (true/false).
`,
subagent_type: "general"
})
Agent({
description: "Apply frontend fixes for ${issueKey}",
prompt: `
Você é o agente de fix frontend do code review ${issueKey}.
Worktree: ${worktreePath}
BLOCKERs a corrigir (somente autoFixable, somente frontend):
${JSON.stringify(frontendBlockers.filter(b => b.autoFixable))}
Para cada blocker: leia SOMENTE o arquivo indicado em `blocker.file`, aplique o fix e salve.
Não leia arquivos que não estejam listados nos blockers acima.
Retorne lista de arquivos corrigidos.
`,
subagent_type: "general"
})
Spawne um subagente de re-validação (após Fix Agents completarem):
Agent({
description: 'Re-validate after fixes for ${issueKey}',
prompt: `
Worktree: ${worktreePath}
Execute em sequência:
cd ${worktreePath} && pnpm lint --fix && pnpm lint 2>&1
cd ${worktreePath} && pnpm type-check 2>&1
cd ${worktreePath} && pnpm test --coverage 2>&1
cd ${worktreePath} && pnpm build 2>&1
Retorne: { allPassed: boolean, details: { lint, typecheck, tests, build } }
`,
subagent_type: 'general',
});
Se allPassed: true: prossiga para commit.
Se algum falhar: pare, reportar erro, não fazer commit.
Agent({
description: 'Commit review fixes for ${issueKey}',
prompt: `
Você é o agente de commit dos fixes do code review ${issueKey}.
Worktree: ${worktreePath}
Branch esperada: ${branchName}
Base branch (PROIBIDO para commit direto): ${baseBranch}
⛔ REGRA CRÍTICA DE SEGURANÇA:
Antes de qualquer git add/commit/push, verifique o branch atual:
cd ${worktreePath} && git rev-parse --abbrev-ref HEAD
SE o resultado for "${baseBranch}" (ex: "prod", "main", "master") → ABORTE IMEDIATAMENTE.
Retorne: { committed: false, aborted: true, reason: "Recusa de commit direto em ${baseBranch}" }
SE o resultado for "${branchName}" → prossiga:
cd ${worktreePath}
git add .
git commit -m "fix(review): address code review findings
Fixed issues:
${fixedList}
refs ${issueKey}
Co-authored-by: Copilot <[email protected]>"
git push origin ${branchName}
Retorne: { committed: true, commitSha: string, pushed: boolean }
`,
subagent_type: 'general',
});
Agent({
description: "Deliver review results to PR and Jira",
prompt: `
Você é o agente de entrega do code review ${issueKey} (PR #${prNumber}).
Dados recebidos (APENAS o necessário):
- prNumber: ${prNumber}
- prUrl: "${prData.url}"
- issueKey: "${issueKey}"
- mode: "${mode}"
- unresolvedBlockers: ${unresolvedBlockers}
- fixesApplied: ${JSON.stringify(fixesApplied)}
- reviewSummary: ${JSON.stringify(reviewSummary)}
(shape: { byDimension: [{dimension, status, blockers, warnings}], acStatus: {satisfied, total, results}, tooling: {lint, typecheck, tests, build}, coverage: {domain, adapters}, dimensionStatus: { codeQuality, security, docsConsistency, testsCoverage, swagger } })
- worktreePath: "${worktreePath}" (usado APENAS para merge/push se necessário)
- branchName: "${branchName}"
- repoPath: "${repoPath}"
## 1. Construir Comentário GitHub
Markdown compacto:
## 🔍 Code Review — ${issueKey}
| Dimensão | Status | BLOCKERs | Warnings |
|----------|--------|----------|----------|
| Code Quality | ✅/❌/⏭ | X | Y |
| Security | ✅/❌/⏭ | X | Y |
| Docs & Tests | ✅/❌/⏭ | X | Y |
### Coverage
- Se reviewSummary.dimensionStatus.testsCoverage === "active":
- Domain/Application: X% (gate: 90%)
- Adapters/Hooks: Y% (gate: 80%)
- Se reviewSummary.dimensionStatus.testsCoverage === "skipped":
- skipped — fora do escopo desta issue/PR
### Acceptance Criteria
- ✅ AC1: texto
- ⚠️ AC2: texto
- ❌ AC3: texto
### Findings Summary
${unresolvedBlockers > 0 ? '❌ ' + unresolvedBlockers + ' BLOCKERs não resolvidos' : '✅ Todos os BLOCKERs resolvidos'}
${fixesApplied.length > 0 ? '**Fixes aplicados:** ' + fixesApplied.map(f => f.title).join(', ') : ''}
---
*Gerado automaticamente por Claude Code task-code-review-flow*
## 2. Update PR Body
gh pr edit ${prNumber} --body "<novo body com review report>"
## 3. Add PR Comment
gh pr comment ${prNumber} --body "<markdown acima>"
## 4. Adicionar Label se Necessário
SE unresolvedBlockers > 0:
gh pr label add ${prNumber} "changes-requested" 2>/dev/null || true
## 5. Jira Comment
Use atlassian-rovo-addCommentToJiraIssue:
cloudId: "ed98e5b7-9db4-4052-9766-68a5c3590ccc"
issueIdOrKey: "${issueKey}"
comment: "${buildJiraComment(mode, unresolvedBlockers, fixesApplied)}"
## 6. Create CONCERNS Issue (apenas se deferredItems válidos existirem)
SE deferredItems.length === 0: pule este passo.
SE deferredItems.length > 0:
Use atlassian-rovo-createJiraIssue:
cloudId: "ed98e5b7-9db4-4052-9766-68a5c3590ccc"
projectKey: "<mesmo projeto da issue original, ex: CORE>"
issueTypeName: "Task"
summary: "[CONCERNS] ${issueKey}: ${deferredItems.length} item(s) para ação manual"
description: """
Concerns identificados durante code review de ${issueKey} que requerem ação manual.
Estes itens estão fora do escopo dos ACs originais mas representam risco real.
Origem: PR #${prNumber} — ${prUrl}
Revisado em: ${new Date().toISOString()}
## Items
${deferredItems.map((item, i) => `
### ${i+1}. [${item.category.toUpperCase()}] ${item.title}
${item.description}
**Por que deferred:** ${item.whyDeferred}
**Sugestão:** ${item.suggestion}
`).join('\n')}
## Ação Requerida
Avaliar e resolver cada item antes de considerar a feature completa em produção.
"""
Retorna: { concernsIssueKey: "CORE-XX", concernsIssueUrl: "..." }
Atualiza comentário Jira com referência:
"⚠️ Concerns isolados para ação manual: ${concernsIssueKey} — ${concernsIssueUrl}"
## 7. Merge PR (quando 0 BLOCKERs — sempre, independente do modo)
⛔ NUNCA execute `git commit` nem `git push` diretamente para ${baseBranch}.
A integração ao ${baseBranch} deve ocorrer EXCLUSIVAMENTE via `gh pr merge`.
SE unresolvedBlockers === 0:
cd ${repoPath}
gh pr merge ${prNumber} --squash --delete-branch
(fallback se squash falhar: gh pr merge ${prNumber} --merge --delete-branch)
SE unresolvedBlockers > 0:
Não faça merge. PR permanece aberto com label "changes-requested".
## 8. Jira Transition → DONE (quando merge realizado)
SE unresolvedBlockers === 0 (após merge):
Use atlassian-rovo-getTransitionsForJiraIssue para buscar transição para "Done"
Use atlassian-rovo-transitionJiraIssue para transicionar
Use atlassian-rovo-addCommentToJiraIssue:
"✅ Code review aprovado e merged para ${baseBranch}!
PR: ${prData.url}
Validações finais:
- Architecture: ✅
- Security: ✅
- Docs & Tests: ✅
- Coverage: domain ${reviewSummary.coverage.domain}
- All ACs: ${reviewSummary.acStatus.satisfied}/${reviewSummary.acStatus.total} ✅
Task completa."
SE unresolvedBlockers > 0:
Use atlassian-rovo-addCommentToJiraIssue:
"⚠️ Code review concluído com ${unresolvedBlockers} BLOCKER(s) não resolvido(s).
PR ${prNumber} marcado como changes-requested.
Corrija os blockers e re-execute o review."
## 9. Retorne ao orquestrador:
{
"prCommented": boolean,
"prBodyUpdated": boolean,
"prMerged": boolean,
"jiraCommented": boolean,
"jiraTransitioned": boolean,
"jiraTransitionedTo": "Done|Code Review|Other",
"concernsIssueCreated": boolean,
"concernsIssueKey": "CORE-XX" | null,
"concernsIssueUrl": string | null,
"mergeCommitSha": string,
"finalStatus": "DONE|DONE_WITH_CONCERNS|CHANGES_REQUESTED"
}
`,
subagent_type: "general"
})
gh pr merge.specs/codebase/ gerados durante review DEVEM ser commitados no PR✅ Code Review Approved — CORE-28 | PR #42
Code Quality ✅ 0 BLOCKERs
Security ⏭ skipped — sem endpoints/DTOs no escopo
Docs & Tests ⏭ skipped — sem docs/swagger/nova lógica | ACs 1/1
Lint ✅ | TypeCheck ✅ | Tests ✅ | Build ✅
PR comentado: github.com/nexo/pulls/42#issuecomment-...
Jira transicionado → Code Review
✅ Code Review — CORE-54 | PR #3 → MERGED
Code Quality ✅ ACs atendidos
Security ✅ ACs atendidos (1 concern arquitetural → CONCERNS)
Docs & Tests ✅ ACs atendidos
⚠️ Concerns criados: CORE-55
→ [SECURITY] Tenant isolation fora do escopo desta task
PR #3 → prod ✅
Jira CORE-54 → DONE ✅
Jira CORE-55 (CONCERNS) criado para ação manual ✅
❌ Code Review — CORE-28 | PR #42
BLOCKERs (2):
[CODE] Prisma importado no domain
[TESTS] Coverage 67% < 90%
Deseja corrigir? [S/N] N
PR marcado como "changes-requested"
Jira comentado (não transicionado)
🤖 Code Review (embedded mode) — CORE-28 | PR #42 → MERGED
Code Quality ✅ 0 BLOCKERs
Security ⏭ skipped — sem endpoints/DTOs no escopo
Docs & Tests ⏭ skipped — sem docs/swagger/nova lógica | ACs 1/1 ✅
PR #42 mergado para PROD
Commit: abc123def456...
Jira CORE-28 → DONE ✅
Task implementada, revisada e deployada com sucesso!
🤖 Code Review (embedded mode) — CORE-28 | PR #42 → MERGED
BLOCKERs detectados (2) → Corrigindo automaticamente
✅ console.log → logger (auto-fixed)
✅ Missing @IsString() em DTO (auto-fixed)
Fixes aplicados: 2 arquivos
Re-validação: Lint ✅ | TypeCheck ✅ | Tests ✅ | Build ✅
PR #42 mergado para PROD
Commit: abc123def456...
Jira CORE-28 → DONE ✅
Task completa com sucesso!
🤖 Code Review (embedded mode) — CORE-28 | PR #42 (NÃO MERGED)
BLOCKERs detectados (2) → Tentando auto-fix
✅ console.log → logger (auto-fixed)
❌ Prisma no domain (não auto-fixável — requer refactor)
Status: 1 BLOCKER não resolvido
PR: NÃO MERGADO
Jira CORE-28 → Code Review (aguardando resolução manual)
Action required: Resolver BLOCKER de arquitetura em domain/
/task-code-review-flow CORE-28
/task-code-review-flow https://gmasson.atlassian.net/browse/CORE-28