Reconcilie features e prontidão como código
O catálogo v2 é o contrato remoto para features, tarefas, cenários, evidências declarativas, riscos, rollout, checklist e sign-offs. Ele valida o grafo completo antes da primeira escrita.
O que uma reconciliação controla
O destino — projeto, portfólio e repositório — precisa existir. Para cada feature enviada, o servidor faz upsert da feature e substitui suas dimensões, checklist, tarefas, vínculos, cenários, evidências de origem catalog, riscos, rollout e sign-offs.
replaceApplicationCatalog=trueTambém remove features do portfólio ausentes no payloadreplaceApplicationCatalog=falsePreserva outras features, mas ainda reconcilia integralmente as features presentesevidência catalogSubstituída pela declaração atualevidência ciPreservada porque pertence a uma execução imutávellimite4 MB, 1–200 features por requisiçãoO PUT não é um PATCH. Gere o arquivo completo de cada feature incluída e revise remoções antes de enviar.
Exemplo completo de entrada
O exemplo abaixo é semanticamente válido: pesos somam 100, score calculado é 60, tarefas possuem contexto e todo cenário está mapeado.
{
"version": 2,
"project": "lb-pay",
"application": "pix-automatico",
"repository": "conta-facil/lb-pay",
"replaceApplicationCatalog": false,
"features": [
{
"id": "LBP-PIX-001",
"useCase": "Pagamento recorrente",
"name": "Autorização de recorrência",
"description": "Permite autorizar e revogar pagamentos recorrentes.",
"status": "EM PROGRESSO",
"score": 60,
"deployWindow": "2026-08-14T22:00:00-03:00",
"dimensions": [
{
"label": "Implementação",
"score": 60,
"position": 1
},
{
"label": "Qualidade",
"score": 50,
"position": 2
}
],
"checklist": [
{
"id": "CHK-PIX-001",
"label": "Runbook revisado por Operações",
"meta": "Responsável: SRE",
"blocking": true,
"completed": false,
"position": 1
}
],
"tasks": [
{
"id": "TSK-PIX-001",
"title": "Criar autorização de recorrência",
"description": "Implementar a criação da autorização pelo recebedor.",
"implementationDetails": "Persistir consentimento, limite e frequência.",
"acceptanceCriteria": [
"Retornar 201",
"Registrar auditoria"
],
"scenarioRefs": [
"CT-PIX-001"
],
"testRefs": [
"e2e/pix-recorrente.spec.ts"
],
"readinessWeight": 60,
"progress": 100,
"remainingWork": "",
"area": "Backend",
"status": "CONCLUÍDA",
"tests": "2/2",
"updatedLabel": "commit a4f21c9",
"position": 1
},
{
"id": "TSK-PIX-002",
"title": "Revogar autorização",
"description": "Impedir novas cobranças após a revogação.",
"implementationDetails": "Invalidar autorização e publicar evento.",
"acceptanceCriteria": [
"Não gerar cobranças futuras",
"Operação idempotente"
],
"scenarioRefs": [
"CT-PIX-002"
],
"testRefs": [],
"readinessWeight": 40,
"progress": 0,
"remainingWork": "Implementar endpoint, evento e testes E2E.",
"area": "Backend",
"status": "NÃO INICIADA",
"tests": "0/2",
"updatedLabel": "catálogo",
"position": 2
}
],
"scenarios": [
{
"id": "CT-PIX-001",
"title": "Criar autorização válida",
"result": "PASS",
"meta": "E2E · homologação",
"position": 1
},
{
"id": "CT-PIX-002",
"title": "Revogar autorização ativa",
"result": "PEND",
"meta": "Aguardando implementação",
"position": 2
}
],
"evidences": [
{
"kind": "JSON",
"title": "Resposta da criação da autorização",
"description": "Comprova o contrato HTTP e o identificador persistido.",
"doubleChecked": true,
"scenarioRef": "CT-PIX-001",
"meta": "homolog · a4f21c9",
"payloadType": "text",
"contentType": "application/json",
"content": "{\"status\":\"ATIVA\",\"authorizationId\":\"auth_123\"}",
"encoding": "utf8",
"sourcePath": "artifacts/authorization.json",
"position": 1
}
],
"risks": [
{
"title": "Cobrança após revogação",
"severity": "ALTO",
"mitigation": "Bloquear no ledger e monitorar eventos órfãos.",
"meta": "owner: time Pix",
"position": 1
}
],
"rollout": [
{
"phase": "Canário 5%",
"scheduledFor": "2026-08-14T22:00:00-03:00",
"active": false,
"environment": "production",
"status": "planned",
"trafficPercentage": 5,
"gateStatus": "pending",
"description": "Recebedores internos por 24 horas.",
"rollbackPlan": "Desativar flag pix_recurring_authorization.",
"position": 1
}
],
"signoffs": [
{
"id": "SIGN-PIX-OPS",
"area": "Operações",
"status": "PENDENTE",
"owner": "sre@empresa.com",
"note": "Aguardando runbook.",
"decidedAt": null,
"position": 1
}
]
}
]
}Envie o arquivo
A chamada exige catalog:write, confiança trusted e vínculo com o mesmo workspace/aplicação/repositório. Uma chave criada manualmente no painel possui apenas test-runs:write; para catálogo remoto use o token OIDC curto do GitHub.
curl --fail-with-body --request PUT "$FEATURE_ROLLOUT_URL/api/v2/catalog" --header "Authorization: Bearer $FEATURE_ROLLOUT_TOKEN" --header 'Content-Type: application/json' --data-binary '@feature-rollout.catalog.json'Interprete a saída
As contagens são do payload reconciliado e podem ser usadas como verificação do pipeline. applicationId é o UUID necessário para APIs de documentação.
{
"kind": "synced",
"applicationId": "2cbb9638-67d5-4c23-90ce-c84fa1f45418",
"featureCount": 1,
"taskCount": 2,
"scenarioCount": 2,
"riskCount": 1,
"rolloutCount": 1
}Corrija violações antes de gravar
DUPLICATE_IDFeature, tarefa ou cenário repetidoREADINESS_UNMAPPEDPesos das tarefas não somam 100SCORE_MISMATCHScore não corresponde a peso × progressoTASK_CONTEXT_MISSINGDescrição, implementação ou aceite ausenteTASK_PROGRESS_INCONSISTENTStatus conclusivo e progresso discordamTASK_REMAINING_WORK_MISSINGTarefa incompleta sem remainingWorkSCENARIO_REFERENCE_NOT_FOUNDTarefa aponta para cenário inexistenteSCENARIO_UNMAPPEDCenário não pertence a nenhuma tarefaREADY_WITH_OPEN_GATEScore 100 com checklist bloqueante aberto{
"error": "Sync recusado: o catálogo não mapeia 100% da prontidão.",
"code": "CATALOG_COMPLETENESS_FAILED",
"violations": [{
"code": "READINESS_UNMAPPED",
"featureId": "LBP-PIX-001",
"missingPoints": 40,
"message": "40 ponto(s) de prontidão não estão mapeados; os pesos somam 60/100."
}]
}Obtenha o token remoto por GitHub OIDC
A política OIDC do repositório valida issuer, audience, repositório, evento, ref e workflow. O token retornado expira em 10 minutos e recebe test-runs:write, catalog:write e rollout:write. Pull requests recebem confiança untrusted e não podem reconciliar catálogo ou avaliar gate.
permissions:
contents: read
id-token: write
steps:
- name: Obter identidade OIDC do GitHub
id: oidc
shell: bash
run: |
oidc_jwt="$(curl --fail --silent -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://feature-rollout.dev" | jq -r .value)"
response="$(curl --fail-with-body --request POST https://feature-rollout.dev/api/v2/auth/github -H "Authorization: Bearer $oidc_jwt")"
echo "token=$(jq -r .token <<<"$response")" >> "$GITHUB_OUTPUT"
- name: Reconciliar catálogo
env:
FEATURE_ROLLOUT_TOKEN: ${{ steps.oidc.outputs.token }}
run: |
curl --fail-with-body --request PUT https://feature-rollout.dev/api/v2/catalog -H "Authorization: Bearer $FEATURE_ROLLOUT_TOKEN" -H "Content-Type: application/json" --data-binary @feature-rollout.catalog.jsonPipeline recomendado
01Validar JSON e regras de completude no pull request02Obter token OIDC em evento/ref confiável03PUT /api/v2/catalog04Executar testes contra o mesmo commit05POST /api/v2/test-runs com X-Run-ID idempotente06POST /api/v2/gates/evaluate07Promover somente com HTTP 200 e passed=true