diff --git a/contracts/domain/comum.schema.json b/contracts/domain/comum.schema.json index fe9ae18..ea2382b 100644 --- a/contracts/domain/comum.schema.json +++ b/contracts/domain/comum.schema.json @@ -84,7 +84,7 @@ "sugestao_adaptacao", "explicacao" ], - "description": "Vocabulário das etapas da trilha no grafo atual. extracao_parametros registra a extração dos parâmetros da regra e validacao_dominio registra sua validação de domínio. confirmacao é gravado pela API quando o usuário corrige a regra. geracao_codigo cobre geração, derivação e correção do código. delegacao_worker grava uma linha por código; as execuções candidatas da busca da meta não geram linha. decisao cobre a decisão sobre o resultado e o encerramento da busca da meta maior. sugestao_adaptacao grava uma linha por resultado inviável analisado. interpretacao_resultado e explicacao estão reservados e nenhum nó os produz hoje." + "description": "Vocabulário das etapas da trilha no grafo atual. extracao_parametros registra a extração dos parâmetros da regra e validacao_dominio registra sua validação de domínio. confirmacao é gravado pela API quando o usuário corrige a regra. geracao_codigo cobre geração, derivação e correção do código. delegacao_worker grava uma linha por código; as execuções candidatas da busca da meta não geram linha. decisao cobre a decisão sobre o resultado e o encerramento da busca da meta maior. sugestao_adaptacao grava uma linha por resultado inviável analisado. explicacao roda depois da decisão, somente em simulação com sucesso, e registra o desfecho da geração e da conferência da narrativa. interpretacao_resultado está reservado e nenhum nó o produz hoje." } } } diff --git a/contracts/domain/resultado-fontes.schema.json b/contracts/domain/resultado-fontes.schema.json new file mode 100644 index 0000000..f69f16b --- /dev/null +++ b/contracts/domain/resultado-fontes.schema.json @@ -0,0 +1,78 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://synapse.local/contracts/domain/resultado-fontes.schema.json", + "title": "Fontes de dados do resultado da simulação", + "description": "Coluna resultados_simulacao.fontes. Identifica as bases que o worker conferiu contra o manifesto antes de executar. As imagens do worker e do sandbox copiam os dados do mesmo diretório do repositório, worker/sandbox/data/domrock. O registro contém somente referências e identificação das bases, sem seu conteúdo. A linha imutável do resultado o vincula ao código, à versão da regra, à simulação e ao horário da execução.", + "type": "object", + "required": ["manifesto", "competencias", "arquivos", "baselines"], + "properties": { + "manifesto": { + "type": "object", + "description": "Identificação do manifesto dos baselines usado na conferência.", + "required": ["versao", "congelamento"], + "properties": { + "versao": { + "type": "integer", + "minimum": 1, + "description": "Versão registrada no manifesto." + }, + "congelamento": { + "type": "string", + "format": "date", + "description": "Data de congelamento registrada no manifesto." + } + } + }, + "competencias": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { "$ref": "comum.schema.json#/$defs/competencia" }, + "description": "Competências históricas processadas pela execução." + }, + "arquivos": { + "type": "array", + "minItems": 1, + "description": "Arquivos de base conferidos pelo worker contra fontes_sha256 do manifesto.", + "items": { + "type": "object", + "required": ["nome", "sha256"], + "properties": { + "nome": { + "type": "string", + "minLength": 1, + "description": "Nome relativo ao diretório das bases, como registrado em fontes_sha256." + }, + "sha256": { "$ref": "#/$defs/sha256" } + } + } + }, + "baselines": { + "type": "array", + "minItems": 1, + "description": "Referências dos baselines congelados das competências processadas, com arquivo e sha256 registrados no manifesto.", + "items": { + "type": "object", + "required": ["competencia", "arquivo", "sha256"], + "properties": { + "competencia": { "$ref": "comum.schema.json#/$defs/competencia" }, + "arquivo": { + "type": "string", + "minLength": 1, + "description": "Caminho do baseline relativo ao diretório das bases, como registrado no manifesto." + }, + "sha256": { "$ref": "#/$defs/sha256" } + } + } + } + }, + "$defs": { + "sha256": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$", + "description": "SHA-256 com 64 caracteres hexadecimais minúsculos." + } + } +} diff --git a/contracts/domain/trilha-conclusao.schema.json b/contracts/domain/trilha-conclusao.schema.json index 88fe123..6e7c24c 100644 --- a/contracts/domain/trilha-conclusao.schema.json +++ b/contracts/domain/trilha-conclusao.schema.json @@ -83,6 +83,11 @@ "minimum": 0, "description": "Nó decisao, na linha que encerra a busca da meta maior. Quantidade de candidatas simuladas na busca. A presença de desfecho_busca distingue esta linha da decisão sobre o resultado. As execuções candidatas não têm linha própria na trilha e ficam em resultados_simulacao com proposito = 'busca_meta'." }, + "desfecho_explicacao": { + "type": "string", + "enum": ["conferida", "reprovada", "sem_resposta", "provedor_indisponivel"], + "description": "Nó explicacao. conferida significa que o texto foi gravado e seus números correspondem aos valores apurados. reprovada significa que o texto foi gravado para auditoria, mas algum número citado não corresponde ao apurado. sem_resposta significa que o modelo não devolveu texto. provedor_indisponivel significa que a indisponibilidade do provedor impediu obter o texto. Só conferida produz narrativa exibível ao profissional de RH. Nos demais desfechos, a falha de explicação degrada a rastreabilidade sem invalidar o resultado da simulação." + }, "encaminhamento": { "type": "string", "description": "Nó decisao: para onde o fluxo seguiu.", diff --git a/contracts/events/no-concluido.schema.json b/contracts/events/no-concluido.schema.json index 9bbe939..83f4907 100644 --- a/contracts/events/no-concluido.schema.json +++ b/contracts/events/no-concluido.schema.json @@ -48,7 +48,7 @@ "resultado_id": { "type": "string", "format": "uuid", - "description": "Referência ao resultado sobre o qual a etapa agiu. Na decisao, é o resultado decidido. Na sugestao_adaptacao, é o resultado inviável analisado. Na linha decisao que encerra a busca da meta maior, é a candidata vencedora; fica ausente quando a busca termina sem solução. Na geracao_codigo com modo_geracao igual a correcao, é o resultado que falhou. Na geracao_codigo com modo_geracao igual a derivado, é o resultado de sucesso da versão de origem. Nas demais etapas, fica ausente." + "description": "Referência ao resultado sobre o qual a etapa agiu. Na decisao, é o resultado decidido. Na sugestao_adaptacao, é o resultado inviável analisado. Na linha decisao que encerra a busca da meta maior, é a candidata vencedora; fica ausente quando a busca termina sem solução. Na geracao_codigo com modo_geracao igual a correcao, é o resultado que falhou. Na geracao_codigo com modo_geracao igual a derivado, é o resultado de sucesso da versão de origem. Na explicacao, é o resultado explicado, inclusive quando a etapa termina sem texto ou com o texto reprovado. Nas demais etapas, fica ausente." }, "prompt_id": { "type": "string", @@ -63,7 +63,7 @@ "explicacao_id": { "type": "string", "format": "uuid", - "description": "Linha de explicacoes produzida pelo nó de explicação. Ausente nos demais nós, e também quando não houve narrativa utilizável (US05, cenário 2)." + "description": "Linha de explicacoes produzida pelo nó explicacao. Presente sempre que a etapa gravou uma linha, tanto com narrativa conferida quanto reprovada na conferência numérica. Ausente nos demais nós e, na explicacao, quando o modelo não devolveu texto, inclusive por indisponibilidade do provedor. A linha da etapa também traz resultado_id com o resultado explicado." } }, "examples": [ diff --git a/contracts/examples/domain/resultado-fontes.json b/contracts/examples/domain/resultado-fontes.json new file mode 100644 index 0000000..e807e5b --- /dev/null +++ b/contracts/examples/domain/resultado-fontes.json @@ -0,0 +1,44 @@ +{ + "manifesto": { + "versao": 2, + "congelamento": "2026-09-17" + }, + "competencias": [ + "2025-10", + "2025-11" + ], + "arquivos": [ + { + "nome": "rh.jsonl", + "sha256": "c52c5955e534e95664239e68ced2e1ca8c649fec8c57a96f4f5195cb112046b9" + }, + { + "nome": "vendas.jsonl", + "sha256": "1a77fb4fd30315cd2acb63a5b09703f0ba74c358efe1a705f685f4f8617fd39a" + }, + { + "nome": "comissoes.jsonl", + "sha256": "602601c76c51fefd1a022c60a29c5c963d1b7996232f5030f454b7e8b10ba3ec" + }, + { + "nome": "eventos_rh.jsonl", + "sha256": "ba3fac163ef6fe98c18a74847dda0b9061fb3fbf8c4610c683d2f64e41af595b" + }, + { + "nome": "regras_competencia.jsonl", + "sha256": "e8d5a18d0d2d0513e18d318cd0b215804595aa49191e82f73fa63ba9b976631f" + } + ], + "baselines": [ + { + "competencia": "2025-10", + "arquivo": "baselines/baseline-2025-10.jsonl", + "sha256": "ce040f196eddcbd5cde89e135f3a06a1250267c0ad81d6bbfe18be2d971f7ee6" + }, + { + "competencia": "2025-11", + "arquivo": "baselines/baseline-2025-11.jsonl", + "sha256": "f8ca7380bd494a455453f847bdbae70f0be4b80034d6cd0430093a3193f7c848" + } + ] +} diff --git a/contracts/examples/domain/trilha-conclusao-explicacao-conferida.json b/contracts/examples/domain/trilha-conclusao-explicacao-conferida.json new file mode 100644 index 0000000..edd91c3 --- /dev/null +++ b/contracts/examples/domain/trilha-conclusao-explicacao-conferida.json @@ -0,0 +1,4 @@ +{ + "resumo": "narrativa gravada e conferida contra os valores apurados", + "desfecho_explicacao": "conferida" +} diff --git a/contracts/examples/domain/trilha-conclusao-explicacao-provedor-indisponivel.json b/contracts/examples/domain/trilha-conclusao-explicacao-provedor-indisponivel.json new file mode 100644 index 0000000..3cf8416 --- /dev/null +++ b/contracts/examples/domain/trilha-conclusao-explicacao-provedor-indisponivel.json @@ -0,0 +1,4 @@ +{ + "resumo": "indisponibilidade do provedor impediu obter a narrativa", + "desfecho_explicacao": "provedor_indisponivel" +} diff --git a/contracts/examples/domain/trilha-conclusao-explicacao-reprovada.json b/contracts/examples/domain/trilha-conclusao-explicacao-reprovada.json new file mode 100644 index 0000000..c81aa55 --- /dev/null +++ b/contracts/examples/domain/trilha-conclusao-explicacao-reprovada.json @@ -0,0 +1,4 @@ +{ + "resumo": "narrativa gravada para auditoria e reprovada por citar número não apurado", + "desfecho_explicacao": "reprovada" +} diff --git a/contracts/examples/domain/trilha-conclusao-explicacao-sem-resposta.json b/contracts/examples/domain/trilha-conclusao-explicacao-sem-resposta.json new file mode 100644 index 0000000..043e9c5 --- /dev/null +++ b/contracts/examples/domain/trilha-conclusao-explicacao-sem-resposta.json @@ -0,0 +1,4 @@ +{ + "resumo": "modelo não devolveu texto para explicar o resultado", + "desfecho_explicacao": "sem_resposta" +} diff --git a/contracts/examples/events/no-concluido-explicacao-conferida.json b/contracts/examples/events/no-concluido-explicacao-conferida.json new file mode 100644 index 0000000..badb195 --- /dev/null +++ b/contracts/examples/events/no-concluido-explicacao-conferida.json @@ -0,0 +1,14 @@ +{ + "evento_id": "4d8e2cbf-da15-5058-8845-8a91b0fdd557", + "job_id": "3f2b1c40-0d18-4a51-9f2e-6c1d9a77b021", + "no": "explicacao", + "concluido_em": "2025-11-24T15:30:01Z", + "conclusao": { + "resumo": "narrativa gravada e conferida contra os valores apurados", + "desfecho_explicacao": "conferida" + }, + "regra_id": "9c7d3e21-4a6b-4c8d-9e0f-1a2b3c4d5e6f", + "resultado_id": "d18f2a47-7db3-4edc-a6b3-4d8900d59a21", + "prompt_id": "1a2b3c4d-5e6f-4071-8293-a4b5c6d7e8f9", + "explicacao_id": "eaa58f82-b012-5023-9ca5-4e28ec727714" +} diff --git a/contracts/examples/events/no-concluido-explicacao-provedor-indisponivel.json b/contracts/examples/events/no-concluido-explicacao-provedor-indisponivel.json new file mode 100644 index 0000000..b811ce7 --- /dev/null +++ b/contracts/examples/events/no-concluido-explicacao-provedor-indisponivel.json @@ -0,0 +1,13 @@ +{ + "evento_id": "b7554168-bf45-5023-8aaa-38e199682151", + "job_id": "3f2b1c40-0d18-4a51-9f2e-6c1d9a77b021", + "no": "explicacao", + "concluido_em": "2025-11-24T15:30:04Z", + "conclusao": { + "resumo": "indisponibilidade do provedor impediu obter a narrativa", + "desfecho_explicacao": "provedor_indisponivel" + }, + "regra_id": "9c7d3e21-4a6b-4c8d-9e0f-1a2b3c4d5e6f", + "resultado_id": "d18f2a47-7db3-4edc-a6b3-4d8900d59a21", + "prompt_id": "1a2b3c4d-5e6f-4071-8293-a4b5c6d7e8f9" +} diff --git a/contracts/examples/events/no-concluido-explicacao-reprovada.json b/contracts/examples/events/no-concluido-explicacao-reprovada.json new file mode 100644 index 0000000..7d20e3f --- /dev/null +++ b/contracts/examples/events/no-concluido-explicacao-reprovada.json @@ -0,0 +1,14 @@ +{ + "evento_id": "1108d50f-ff2d-58dd-b427-c38adbdc137c", + "job_id": "3f2b1c40-0d18-4a51-9f2e-6c1d9a77b021", + "no": "explicacao", + "concluido_em": "2025-11-24T15:30:02Z", + "conclusao": { + "resumo": "narrativa gravada para auditoria e reprovada por citar número não apurado", + "desfecho_explicacao": "reprovada" + }, + "regra_id": "9c7d3e21-4a6b-4c8d-9e0f-1a2b3c4d5e6f", + "resultado_id": "d18f2a47-7db3-4edc-a6b3-4d8900d59a21", + "prompt_id": "1a2b3c4d-5e6f-4071-8293-a4b5c6d7e8f9", + "explicacao_id": "bfec331a-9f2b-5143-81f4-702e7556d27c" +} diff --git a/contracts/examples/events/no-concluido-explicacao-sem-resposta.json b/contracts/examples/events/no-concluido-explicacao-sem-resposta.json new file mode 100644 index 0000000..0926208 --- /dev/null +++ b/contracts/examples/events/no-concluido-explicacao-sem-resposta.json @@ -0,0 +1,13 @@ +{ + "evento_id": "01f35e9f-f672-56d3-bcc5-4c67f5d0a470", + "job_id": "3f2b1c40-0d18-4a51-9f2e-6c1d9a77b021", + "no": "explicacao", + "concluido_em": "2025-11-24T15:30:03Z", + "conclusao": { + "resumo": "modelo não devolveu texto para explicar o resultado", + "desfecho_explicacao": "sem_resposta" + }, + "regra_id": "9c7d3e21-4a6b-4c8d-9e0f-1a2b3c4d5e6f", + "resultado_id": "d18f2a47-7db3-4edc-a6b3-4d8900d59a21", + "prompt_id": "1a2b3c4d-5e6f-4071-8293-a4b5c6d7e8f9" +} diff --git a/contracts/examples/manifesto.json b/contracts/examples/manifesto.json index 39dd670..7561c77 100644 --- a/contracts/examples/manifesto.json +++ b/contracts/examples/manifesto.json @@ -68,6 +68,15 @@ "events/meta-venda-sugerida.json": "events/meta-venda-sugerida.schema.json", "events/meta-venda-sugerida-sem-solucao.json": "events/meta-venda-sugerida.schema.json", "events/sugestao-adaptacao-proposta-tentativa.json": "events/sugestao-adaptacao-proposta.schema.json", - "events/etapa-alterada-sem-alternativa.json": "events/etapa-alterada.schema.json" + "events/etapa-alterada-sem-alternativa.json": "events/etapa-alterada.schema.json", + "domain/resultado-fontes.json": "domain/resultado-fontes.schema.json", + "domain/trilha-conclusao-explicacao-conferida.json": "domain/trilha-conclusao.schema.json", + "events/no-concluido-explicacao-conferida.json": "events/no-concluido.schema.json", + "domain/trilha-conclusao-explicacao-reprovada.json": "domain/trilha-conclusao.schema.json", + "events/no-concluido-explicacao-reprovada.json": "events/no-concluido.schema.json", + "domain/trilha-conclusao-explicacao-sem-resposta.json": "domain/trilha-conclusao.schema.json", + "events/no-concluido-explicacao-sem-resposta.json": "events/no-concluido.schema.json", + "domain/trilha-conclusao-explicacao-provedor-indisponivel.json": "domain/trilha-conclusao.schema.json", + "events/no-concluido-explicacao-provedor-indisponivel.json": "events/no-concluido.schema.json" } } diff --git a/contracts/http/openapi.yaml b/contracts/http/openapi.yaml index c126992..3a9a755 100644 --- a/contracts/http/openapi.yaml +++ b/contracts/http/openapi.yaml @@ -1334,7 +1334,12 @@ paths: `regras` traz as representações estruturadas já formadas. **Depois vêm os eventos conforme acontecem**: `etapa` a cada passo iniciado, - `estado` a cada transição do job e `resultado` quando a simulação termina. O + `estado` a cada transição do job, `resultado` quando a simulação termina e + `explicacao` quando a etapa de explicação termina. A explicação pode chegar + alguns segundos depois do resultado: o evento traz `job_id`, `simulacao_id` + e `disponivel`, nunca o texto. `disponivel` é verdadeiro somente quando a + narrativa foi conferida. Ao recebê-lo, o cliente busca `GET /jobs/{id}` + novamente para atualizar a narrativa e a flag de baixa rastreabilidade. O stream fecha logo após o evento de estado que anuncia um estado terminal; abrir o stream de um job já terminal devolve a fotografia e fecha em seguida. @@ -1369,7 +1374,7 @@ paths: $ref: "#/components/schemas/EventoProgresso" examples: streamDeUmJob: - summary: Sequência típica, da conexão ao resultado + summary: Sequência típica, da conexão ao resultado e à explicação description: | O corpo inteiro da resposta, e não um evento isolado. O primeiro evento é a fotografia recebida ao conectar; os demais chegam @@ -1397,6 +1402,10 @@ paths: event: estado id: 5 data: {"job_id":"3f2b1c40-0d18-4a51-9f2e-6c1d9a77b021","status":"simulacao_inviavel","status_anterior":"simulando"} + + event: explicacao + id: 6 + data: {"job_id":"3f2b1c40-0d18-4a51-9f2e-6c1d9a77b021","simulacao_id":"b81e0f4c-52a9-4f0b-8a3d-7c2e5d10ab93","disponivel":true} "401": $ref: "#/components/responses/NaoAutenticado" "403": @@ -2588,6 +2597,13 @@ components: $ref: ../domain/resultado-decomposicao.schema.json diagnostico: $ref: ../domain/resultado-diagnostico.schema.json + fontes: + $ref: ../domain/resultado-fontes.schema.json + description: | + Identificação das bases conferidas pelo worker para esta execução. + Campo opcional para compatibilidade: ausente nos resultados gravados + antes de sua introdução e nos resultados com status `erro_infra`. + Contém referências e hashes, nunca o conteúdo das bases. criado_em: type: string format: date-time @@ -3739,10 +3755,33 @@ components: flag_baixa_rastreabilidade: type: boolean description: | - Verdadeiro quando o modelo não devolveu justificativa utilizável ou quando - a narrativa reprovou na conferência numérica. Falha de - explicação degrada a rastreabilidade e não invalida o número; falha de - cobertura ou de asserção invalida. + Verdadeiro quando a etapa de explicação terminou sem texto, inclusive + por indisponibilidade do provedor, ou com o texto reprovado na + conferência numérica. Falso enquanto a explicação não chegou e quando + a narrativa foi conferida. Falha de explicação degrada a + rastreabilidade e não invalida o número; falha de cobertura ou de + asserção invalida. + explicacao: + type: object + description: | + Narrativa conferida do resultado vigente desta simulação. Campo + opcional, presente somente quando a explicação desse resultado foi + conferida. Ausente enquanto a explicação não chegou, quando não houve + texto, quando a narrativa foi reprovada na conferência numérica e + quando o resultado vigente é uma meta aceita. Nunca nulo. + required: [texto, criada_em] + properties: + texto: + type: string + minLength: 1 + description: Narrativa com aderência numérica conferida contra o resultado explicado. + criada_em: + type: string + format: date-time + description: Instante de criação da explicação, de explicacoes.criado_em. + examples: + - texto: "A simulação aplicou a regra às bases históricas e foi considerada inviável para o orçamento informado." + criada_em: "2025-11-24T15:30:05Z" resultado: $ref: "../domain/resultado-simulacao.schema.json" description: | @@ -3950,9 +3989,9 @@ components: title: Evento do stream de progresso description: | JSON de uma linha `data:` do stream. O nome do evento vai na linha `event:` e - diz qual das três formas chegou: `etapa`, `estado` ou `resultado`. + diz qual das quatro formas chegou: `etapa`, `estado`, `resultado` ou `explicacao`. - Os três têm `job_id`, que é redundante com o caminho e existe assim mesmo - + Os quatro têm `job_id`, que é redundante com o caminho e existe assim mesmo - um cliente que mantém mais de um stream aberto não precisa se apoiar em qual conexão entregou a mensagem. @@ -3964,6 +4003,7 @@ components: - $ref: "#/components/schemas/EventoEtapa" - $ref: "#/components/schemas/EventoEstado" - $ref: "#/components/schemas/EventoResultado" + - $ref: "#/components/schemas/EventoExplicacao" EventoEtapa: type: object @@ -4079,6 +4119,36 @@ components: status: simulacao_inviavel status_anterior: simulando + EventoExplicacao: + type: object + title: Evento `explicacao` + description: | + Anuncia a conclusão da etapa de explicação da simulação. Publicado pela + API a partir da conclusão produzida pelo codegen, carrega referências e + disponibilidade, nunca o texto. A tela busca `GET /jobs/{id}` novamente + para atualizar a narrativa e a flag de baixa rastreabilidade. + required: [job_id, simulacao_id, disponivel] + properties: + job_id: + type: string + format: uuid + simulacao_id: + type: string + format: uuid + disponivel: + type: boolean + description: | + Verdadeiro quando a explicação foi conferida. Falso quando a etapa + terminou com narrativa reprovada, sem resposta ou com o provedor + indisponível. Não carrega a narrativa reprovada. + examples: + - job_id: "3f2b1c40-0d18-4a51-9f2e-6c1d9a77b021" + simulacao_id: "b81e0f4c-52a9-4f0b-8a3d-7c2e5d10ab93" + disponivel: true + - job_id: "3f2b1c40-0d18-4a51-9f2e-6c1d9a77b021" + simulacao_id: "b81e0f4c-52a9-4f0b-8a3d-7c2e5d10ab93" + disponivel: false + EventoResultado: type: object title: Evento `resultado` diff --git a/docs/decisoes/dec-087.md b/docs/decisoes/dec-087.md index 67bcf8c..cb4fb7c 100644 --- a/docs/decisoes/dec-087.md +++ b/docs/decisoes/dec-087.md @@ -27,7 +27,7 @@ Apenas os seguintes papéis existirão na configuração do sistema (API, Fronte | Iniciar fluxo de simulação via voz | ✅ | ❌ | | Receber/aceitar sugestões de adaptação | ✅ | ❌ | | Consultar trilha/logs e fontes via API/Banco | ❌ | ✅ | -| Acessar explicação gerada pela IA para conferência | ❌ | ✅ | +| Acessar explicação gerada pela IA para conferência | ✅ Somente narrativa conferida, nos próprios jobs | ✅ Inclui narrativa reprovada, via auditoria | | Validar/corrigir dados transcritos | ✅ | ❌ | | Gerar, visualizar e arquivar relatórios | ✅ | ❌ | | *Bloqueio de Liberação por Inviabilidade de Orçamento (US-01)* | ⛔ Bloqueado para todos. | ⛔ Bloqueado para todos. | @@ -81,12 +81,18 @@ A consulta individual reúne o nível 3 da explicabilidade. As entradas, versõe | **US-02** | Captura de voz e transcrição | `PROFISSIONAL DE RH` | | **US-03** | Sugestão automática de adaptação | `PROFISSIONAL DE RH` | | **US-04** | Registro de auditoria e fontes de dados | `AUDITOR` | -| **US-05** | Explicabilidade e rastreabilidade da IA | `AUDITOR` | +| **US-05** | Explicabilidade e rastreabilidade da IA | `PROFISSIONAL DE RH` e `AUDITOR`, respeitando o acesso de cada papel | | **US-06** | Validação de dados obtidos por voz | `PROFISSIONAL DE RH` | | **US-07** | Relatórios pós-processamento | `PROFISSIONAL DE RH` | --- +## Acesso à explicação da simulação (T-315 / T-316) + +O profissional de RH acessa a narrativa conferida do resultado vigente dos próprios jobs em `GET /jobs/{id}`, no campo `simulacao.explicacao`. O campo fica ausente enquanto a explicação não chegou, quando não houve texto, quando a narrativa foi reprovada na conferência numérica e quando o resultado vigente é uma meta aceita. O evento `explicacao` do stream avisa a tela para consultar o job novamente; carrega referências e disponibilidade, nunca a narrativa. + +O auditor acessa as narrativas gravadas, conferidas ou reprovadas, pela seção `explicacoes` de `GET /auditoria/jobs/{id}`, com a indicação de `aderencia_conferida`. A narrativa reprovada é preservada para investigação e não é exibida ao profissional de RH. Se não houve texto, não há linha em `explicacoes`; o desfecho fica registrado na conclusão da etapa. O acesso dos dois papéis à explicação não muda a matriz HTTP: o auditor continua sem acesso às rotas operacionais `/jobs/**`. + ## Critérios Satisfeitos * A leitura da trilha via API pelo auditor e a extração da explicação pelo RH estão contempladas na matriz. * O bloqueio de liberação de regra inviável (orçamento) está explícito na matriz, aplicando-se de forma sistêmica (sem override de papel).