Skip to content

feat: diagnostico estruturado de pathwayDiagnostics em execute-candidates (fecha LayoutParserReact#86) - #200

Merged
elson-vinicius-lopes merged 5 commits into
developfrom
feat/execute-candidates-diagnostico-estruturado-86
Aug 27, 2026
Merged

elson-vinicius-lopes merged 5 commits into
developfrom
feat/execute-candidates-diagnostico-estruturado-86

Conversation

@elson-vinicius-lopes

Copy link
Copy Markdown
Collaborator

Resumo

Referencia/resolve LayoutParser/LayoutParserReact#86 (candidates vazio sem diagnóstico estruturado no execute-candidates).

Causa raiz

O bug de "candidates vazio" propriamente dito já havia sido corrigido antes da issue ser aberta. O gap real era a ausência de um contrato estruturado de diagnóstico: quando um pathway (Pathway 1/Pathway 2) não produzia candidato, a API não expunha por quê de forma legível/correlacionável — só logs internos, sem retorno estruturado ao consumidor (BFF/front).

Contrato novo

  • pathwayDiagnostics[] no payload de resposta do execute-candidates, um item por pathway avaliado, com taxonomia de status/code (ex.: not_applicable, failed, sucesso).
  • correlationId propagado ponta a ponta (resposta + logging estruturado), permitindo cruzar a resposta HTTP com os logs do servidor para o mesmo request.
  • Logging estruturado (ILogger, mensagens com parâmetros nomeados) nos ramos not_applicable/failed de cada pathway, incluindo o correlationId.
  • Fix de sanitização do fragmento tcl-xsl que atravessava os diagnósticos (evita quebra de contrato quando o conteúdo do XSLT/TCL contém caracteres problemáticos).

Documentação

Diagnóstico completo do investigação em docs/architecture/diagnostico-issue-86-diagnostico-estruturado-execute-candidates.md, além de atualização de Swagger/README cobrindo o novo formato de resposta.

Testes

  • dotnet build: 0 erros (só warnings pré-existentes, SCS0005 etc.)
  • dotnet test: 399 passando (388 + 11 XslSynth.Core.Tests), 4 falhas pré-existentes de path Windows×Linux (não relacionadas a esta mudança — SafePathResolverTests, LowCodeRunnerArgsTests x3)
  • Novos testes desta branch: TransformationExecutionControllerPathwayDiagnosticsTests (cobrindo os novos ramos de diagnóstico estruturado)

Test plan

  • dotnet build limpo
  • dotnet test sem regressão (mesmas 4 falhas pré-existentes de path)
  • Revisão do dono antes de merge (merge NÃO será feito por este PR)

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com

elson-vinicius-lopes and others added 4 commits August 27, 2026 17:37
… em execute-candidates (issue #86)

Adiciona campo aditivo pathwayDiagnostics[] (vazio nesta etapa, populacao por
pathway fica para @lp-parser-llm) e correlationId ao response de
execute-candidates, conforme desenho em
docs/architecture/diagnostico-issue-86-diagnostico-estruturado-execute-candidates.md.
Corrige tambem sanitizacao ausente no pathway tcl-xsl (3 pontos onde
ex.Message/pipelineResult.Errors iam crus para o wire), alinhando com o
padrao ja usado no pathway sysmiddle (LowCodeErrorSanitizer.ForWire).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
)

Materializa o diagnóstico estruturado em execute-candidates: sysmiddle,
tcl-xsl e ai-fallback agora sempre terminam em exatamente 1 PathwayDiagnostic
(candidate_generated/not_applicable/failed), nunca silenciosos. Diferencia
map_not_found de xsl_not_found via novo TransformationPipelineResult.ErrorCode
(populado na origem, não por regex sobre a mensagem).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…xecute-candidates

Issue LayoutParserReact #86: enriquece o XML doc do endpoint
POST /api/transformationexecution/execute-candidates e adiciona seção
bilíngue PT/EN no README com o contrato aditivo (taxonomia de status/code,
semântica de candidates vazio com causa e a regra de sanitização de mensagens).
… not_applicable/failed

QA (issue #86) apontou gap: os ramos de negócio que adicionam PathwayDiagnostic
(sem mapper, MAP/XSL não encontrado, cooldown de IA, etc.) só escreviam em
warnings/pathwayDiagnostics, sem log correlacionável — só exceções reais geravam
log. Isso deixava o caso mais comum do bug relatado (candidates vazio por falta
de mapper/arquivo) sem rastro em log, só na resposta HTTP.

Adiciona LogInformation (not_applicable/candidate_generated) e LogWarning
(failed) ao lado de cada PathwayDiagnostic.Add, com CorrelationId, pathway,
status, code e a fonte da decisão (catálogo, gate de cooldown, ErrorCode do
pipeline) quando disponível. Reforça os logs já existentes nos blocos catch
com os mesmos campos estruturados.
@github-actions

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

…sercao no PR #200 (falso positivo por deslocamento, sem codigo novo)
@elson-vinicius-lopes
elson-vinicius-lopes merged commit 3ffc5ef into develop Aug 27, 2026
4 checks passed
@elson-vinicius-lopes
elson-vinicius-lopes deleted the feat/execute-candidates-diagnostico-estruturado-86 branch August 27, 2026 22:19
elson-vinicius-lopes added a commit that referenced this pull request Aug 28, 2026
…andidates-141

Reconcilia PR #207 (issue #141) com develop, que já absorveu as PRs
irmãs #200 (issue #86), #201 (issue #139), #203 (issue #138) e #205
(issue #140) da mesma cadeia de trabalho. Conflitos eram todos overlap
real entre PRs desta cadeia tocando os mesmos arquivos, não clash
semântico:

- LowCodeCandidateResult.cs: DecryptedMapperContent (#141) e
  MapperDecryptedContent (#138) eram o mesmo dado (mapper.DecryptedContent)
  sob nomes diferentes — unificado em DecryptedMapperContent, único campo,
  usado tanto por SysmiddleSectionMappingResolver (#138) quanto por
  TryComposeFieldMappings (#141).
- LowCodeAutoTransformationService.cs: mesma duplicação de atribuição
  nos dois pontos de criação de LowCodeCandidateResult.
- TransformationExecutionController.cs: TransformationCandidate agora
  preenche FieldMappings (#141) E SectionMappings/XmlNamespaces (#138)
  no mesmo objeto — funcionalidades complementares, ambas preservadas.
- README.md: seções de documentação de fieldMappings (#141) e
  sectionMappings (#138) são independentes, mantidas as duas em sequência.
- security-code-scan-baseline.json: entradas de linha para
  LowCodeAutoTransformationService.cs reconciliadas para 371/415 (linhas
  atuais pós-merge) — mesmos 2 achados de sempre (File.WriteAllTextAsync
  em inPath/metaPath), não vulnerabilidades novas. Nota adicionada ao
  _readme documentando o ajuste.

dotnet build: 0 erros. dotnet test: 413/417 passando — as 4 falhas
(SafePathResolverTests, LowCodeRunnerArgsTests) são pré-existentes,
específicas de ambiente (assumem paths Windows, falham sob WSL/Linux),
não relacionadas aos arquivos deste merge.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
elson-vinicius-lopes added a commit that referenced this pull request Sep 7, 2026
…pace-200

test(mapping-drafts): isolamento cross-workspace no MappingDraftsController (#200)
elson-vinicius-lopes added a commit that referenced this pull request Sep 16, 2026
…lementados (issue #413) (#419)

Cross-check de 2026-09-15 confirmou que 6 dos 8 itens da tabela original do gate #200
já estão implementados sem código novo: GET /api/workspaces/me, pacote fiscal
versionado, MappingDraft (FiscalProfile, editor manual de artefato, filtros de
release, arquivamento), contrato de saída TCL/XSL/XSLT (hash/status/imutabilidade),
MappingExplanation e o Fiscal Test Lab (diff por regra, diff release×release,
cobertura de obrigatórios). Documenta o gap real que sobra (capability Sysmiddle
como payload consultável, issue #415) sem tratá-lo como resolvido.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
elson-vinicius-lopes added a commit that referenced this pull request Sep 16, 2026
* feat(xml-analysis): endpoint de reconstrução reversa + validação contra TXT original (issue #151) (#407)

* feat(xml-analysis): fecha issue #151 — endpoint de reconstrução reversa + validação contra TXT original

ReverseReconstructionService (best-effort XML->TXT) já existia mas não estava
plugado em nenhum controller/DI. Adiciona POST api/xml-analysis/reverse-reconstruct,
que recebe LayoutXml + o crosswalk FieldToXmlMapping[] já resolvido + TargetXml, e
devolve o TXT reconstruído.

Fecha o gap real da issue (item 3 do critério de aceite): quando OriginalTxt é
informado, ReverseReconstructionValidator compara campo a campo o valor escrito
(via XML) contra o valor real parseado do TXT original (mesmo ILayoutParserService
do pathway direto) — sem ele, a resposta não finge validação nenhuma.

ReconstructionResult ganha ReconstructedFields (issue #151) para dar a
granularidade por campo que a validação precisa, sem reparsear ReconstructedText.

Layout com WithBreakLines=false (MQSeries/IDOC) vira scopeWarning, não erro —
mantém o escopo MVP já declarado no serviço.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* chore(security): baseline do SCS0003 no reverse-reconstruct (issue #151)

O gate de CI (Security Code Scan - gate por severidade) bloqueou o PR #407 por
um achado novo: SCS0003 (XPath injection) em
Services/XmlAnalysis/ReverseReconstructionService.cs:158
(navigator.SelectSingleNode(target.Xpath)).

target.Xpath vem do corpo do POST api/xml-analysis/reverse-reconstruct, mas tanto
ele quanto o TargetXml contra o qual roda vem da MESMA request do MESMO chamador
autenticado - sem cenario de vazamento cross-documento. E leitura pura, ja dentro
de try/catch(XPathException). Aceito conscientemente como debito de baixo risco,
documentado no proprio finding.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* feat(ai): preferências de usuário além do prompt customizado (issue #322) (#409)

* feat(ai): preferências de usuário além do prompt customizado (issue #322)

Adiciona idioma de exibição, nível de detalhe da explicação e engine padrão
(TCL/XSLT) como preferências persistentes em tbLpAiUserSession (colunas novas
via ALTER idempotente), com upsert parcial (SetPreferencesAsync) e leitura com
defaults (GetPreferencesAsync). Endpoint dedicado PUT/GET api/transformation/
ai-preferences, [Authorize] + fail-closed 404 sem identidade, 422 para engine/
nível de detalhe fora dos valores aceitos.

O prompt customizado (ai-prompt-adicional, issue #98) continua no
AiUserInstructionStore em memória — não migrado para o SQL nesta issue, só
exposto junto na leitura de GetAiPreferences para consulta única.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(memory): registra decisão dos dois stores de sessão de IA (issue #322)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* docs(architecture): cross-check gate React#200/epic #368 vs estado atual da API (#412)

6 dos 8 itens originais ja estao implementados (workspaces/me, pacote fiscal,
MappingDraft, contrato de saida TCL/XSL/XSLT, MappingExplanation, Test Lab
diff+cobertura), 1 esta parcial (OpenAPI gerado mas sem contract-check em CI)
e 1 revela gap pequeno nao previsto (capability como payload consultavel).

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* docs: fechar gate React#200/epic #368 — documentar 6 contratos já implementados (issue #413) (#419)

Cross-check de 2026-09-15 confirmou que 6 dos 8 itens da tabela original do gate #200
já estão implementados sem código novo: GET /api/workspaces/me, pacote fiscal
versionado, MappingDraft (FiscalProfile, editor manual de artefato, filtros de
release, arquivamento), contrato de saída TCL/XSL/XSLT (hash/status/imutabilidade),
MappingExplanation e o Fiscal Test Lab (diff por regra, diff release×release,
cobertura de obrigatórios). Documenta o gap real que sobra (capability Sysmiddle
como payload consultável, issue #415) sem tratá-lo como resolvido.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* feat(mapping-drafts): endpoint de listagem por workspace (issue #416) (#420)

Front reportou gap: Mapping Studio exigia digitar o GUID do draft manualmente,
sem forma de descobrir/listar. Adiciona GET .../mapping-drafts espelhando o
padrão de descoberta já usado em mapping-releases (issue #377): paginação,
RBAC de leitura (qualquer papel de membro) e filtro opcional por engine.
Draft não tem "status" próprio (só as regras têm), então não há filtro de
status neste endpoint.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
elson-vinicius-lopes added a commit that referenced this pull request Sep 16, 2026
* feat(xml-analysis): endpoint de reconstrução reversa + validação contra TXT original (issue #151) (#407)

* feat(xml-analysis): fecha issue #151 — endpoint de reconstrução reversa + validação contra TXT original

ReverseReconstructionService (best-effort XML->TXT) já existia mas não estava
plugado em nenhum controller/DI. Adiciona POST api/xml-analysis/reverse-reconstruct,
que recebe LayoutXml + o crosswalk FieldToXmlMapping[] já resolvido + TargetXml, e
devolve o TXT reconstruído.

Fecha o gap real da issue (item 3 do critério de aceite): quando OriginalTxt é
informado, ReverseReconstructionValidator compara campo a campo o valor escrito
(via XML) contra o valor real parseado do TXT original (mesmo ILayoutParserService
do pathway direto) — sem ele, a resposta não finge validação nenhuma.

ReconstructionResult ganha ReconstructedFields (issue #151) para dar a
granularidade por campo que a validação precisa, sem reparsear ReconstructedText.

Layout com WithBreakLines=false (MQSeries/IDOC) vira scopeWarning, não erro —
mantém o escopo MVP já declarado no serviço.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* chore(security): baseline do SCS0003 no reverse-reconstruct (issue #151)

O gate de CI (Security Code Scan - gate por severidade) bloqueou o PR #407 por
um achado novo: SCS0003 (XPath injection) em
Services/XmlAnalysis/ReverseReconstructionService.cs:158
(navigator.SelectSingleNode(target.Xpath)).

target.Xpath vem do corpo do POST api/xml-analysis/reverse-reconstruct, mas tanto
ele quanto o TargetXml contra o qual roda vem da MESMA request do MESMO chamador
autenticado - sem cenario de vazamento cross-documento. E leitura pura, ja dentro
de try/catch(XPathException). Aceito conscientemente como debito de baixo risco,
documentado no proprio finding.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* feat(ai): preferências de usuário além do prompt customizado (issue #322) (#409)

* feat(ai): preferências de usuário além do prompt customizado (issue #322)

Adiciona idioma de exibição, nível de detalhe da explicação e engine padrão
(TCL/XSLT) como preferências persistentes em tbLpAiUserSession (colunas novas
via ALTER idempotente), com upsert parcial (SetPreferencesAsync) e leitura com
defaults (GetPreferencesAsync). Endpoint dedicado PUT/GET api/transformation/
ai-preferences, [Authorize] + fail-closed 404 sem identidade, 422 para engine/
nível de detalhe fora dos valores aceitos.

O prompt customizado (ai-prompt-adicional, issue #98) continua no
AiUserInstructionStore em memória — não migrado para o SQL nesta issue, só
exposto junto na leitura de GetAiPreferences para consulta única.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(memory): registra decisão dos dois stores de sessão de IA (issue #322)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* docs(architecture): cross-check gate React#200/epic #368 vs estado atual da API (#412)

6 dos 8 itens originais ja estao implementados (workspaces/me, pacote fiscal,
MappingDraft, contrato de saida TCL/XSL/XSLT, MappingExplanation, Test Lab
diff+cobertura), 1 esta parcial (OpenAPI gerado mas sem contract-check em CI)
e 1 revela gap pequeno nao previsto (capability como payload consultavel).

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* docs: fechar gate React#200/epic #368 — documentar 6 contratos já implementados (issue #413) (#419)

Cross-check de 2026-09-15 confirmou que 6 dos 8 itens da tabela original do gate #200
já estão implementados sem código novo: GET /api/workspaces/me, pacote fiscal
versionado, MappingDraft (FiscalProfile, editor manual de artefato, filtros de
release, arquivamento), contrato de saída TCL/XSL/XSLT (hash/status/imutabilidade),
MappingExplanation e o Fiscal Test Lab (diff por regra, diff release×release,
cobertura de obrigatórios). Documenta o gap real que sobra (capability Sysmiddle
como payload consultável, issue #415) sem tratá-lo como resolvido.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* feat(mapping-drafts): endpoint de listagem por workspace (issue #416) (#420)

Front reportou gap: Mapping Studio exigia digitar o GUID do draft manualmente,
sem forma de descobrir/listar. Adiciona GET .../mapping-drafts espelhando o
padrão de descoberta já usado em mapping-releases (issue #377): paginação,
RBAC de leitura (qualquer papel de membro) e filtro opcional por engine.
Draft não tem "status" próprio (só as regras têm), então não há filtro de
status neste endpoint.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* fix(deploy): portar migracao NSSM -> servico nativo para producao (#426)

deploy.yml nunca teve a logica de deteccao/migracao do servico legado
NSSM (C:\Users\Administrator\Downloads\nssm-2.24) que o ci-dev.yml ja
usa a cada deploy de dev - producao so fazia Stop-Service/Start-Service
genericos, sem checar o ImagePath do registro nem recriar como servico
Windows nativo (Program.cs usa UseWindowsService() ha tempo).

Porta a mesma logica do ci-dev.yml: no stop, checa se o ImagePath
contem "nssm" e, se sim, remove o registro (sc.exe delete); no restart,
recria o servico nativo (sc.exe create/description/failure) caso ele
nao exista - cobrindo tanto a migracao quanto o primeiro deploy num
host novo. Como sc.exe delete apaga a chave Environment que o step
"Configurar ambiente do servico" ja tinha escrito antes nesse mesmo
registro NSSM, captura o Environment antes do delete e restaura apos
criar o servico nativo, para nao perder Database__Password/
IdentityDatabase__*/etc. na primeira subida.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* feat(fiscal): endpoint de arvore de layout dupla origem/destino (issue #425) (#427)

Implementa GET /api/workspaces/{workspaceId}/mappings/{mappingId}/layout-tree
conforme o ADR "adr-layout-tree-endpoint-425": generaliza GuidXPathCatalog
(ai/XslSynth.Contracts) com extracao de cardinalidade (MinimalOccurrence/
MaximumOccurrence) e um novo BuildTree recursivo (achatando wrappers Choice/
Sequence como irmaos, mesma convencao do XPath ja existente), troca a fonte
de arquivo local por lookup via ICachedLayoutService/ICachedMapperService ja
registrados em producao, e expoe o contrato source/target/rules para o React
replicar a UI de dupla-arvore do Connect Us.

Regras[] reaproveitam apenas LinkMappingItemVO (unico ponto do MapperVO real
onde origem e destino sao GUIDs de no, nao path de DSL) - decisao explicita
do ADR pra nao inventar GUID de origem para as regras DSL (MapperRule).

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
elson-vinicius-lopes added a commit that referenced this pull request Sep 16, 2026
* feat(xml-analysis): endpoint de reconstrução reversa + validação contra TXT original (issue #151) (#407)

* feat(xml-analysis): fecha issue #151 — endpoint de reconstrução reversa + validação contra TXT original

ReverseReconstructionService (best-effort XML->TXT) já existia mas não estava
plugado em nenhum controller/DI. Adiciona POST api/xml-analysis/reverse-reconstruct,
que recebe LayoutXml + o crosswalk FieldToXmlMapping[] já resolvido + TargetXml, e
devolve o TXT reconstruído.

Fecha o gap real da issue (item 3 do critério de aceite): quando OriginalTxt é
informado, ReverseReconstructionValidator compara campo a campo o valor escrito
(via XML) contra o valor real parseado do TXT original (mesmo ILayoutParserService
do pathway direto) — sem ele, a resposta não finge validação nenhuma.

ReconstructionResult ganha ReconstructedFields (issue #151) para dar a
granularidade por campo que a validação precisa, sem reparsear ReconstructedText.

Layout com WithBreakLines=false (MQSeries/IDOC) vira scopeWarning, não erro —
mantém o escopo MVP já declarado no serviço.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* chore(security): baseline do SCS0003 no reverse-reconstruct (issue #151)

O gate de CI (Security Code Scan - gate por severidade) bloqueou o PR #407 por
um achado novo: SCS0003 (XPath injection) em
Services/XmlAnalysis/ReverseReconstructionService.cs:158
(navigator.SelectSingleNode(target.Xpath)).

target.Xpath vem do corpo do POST api/xml-analysis/reverse-reconstruct, mas tanto
ele quanto o TargetXml contra o qual roda vem da MESMA request do MESMO chamador
autenticado - sem cenario de vazamento cross-documento. E leitura pura, ja dentro
de try/catch(XPathException). Aceito conscientemente como debito de baixo risco,
documentado no proprio finding.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* feat(ai): preferências de usuário além do prompt customizado (issue #322) (#409)

* feat(ai): preferências de usuário além do prompt customizado (issue #322)

Adiciona idioma de exibição, nível de detalhe da explicação e engine padrão
(TCL/XSLT) como preferências persistentes em tbLpAiUserSession (colunas novas
via ALTER idempotente), com upsert parcial (SetPreferencesAsync) e leitura com
defaults (GetPreferencesAsync). Endpoint dedicado PUT/GET api/transformation/
ai-preferences, [Authorize] + fail-closed 404 sem identidade, 422 para engine/
nível de detalhe fora dos valores aceitos.

O prompt customizado (ai-prompt-adicional, issue #98) continua no
AiUserInstructionStore em memória — não migrado para o SQL nesta issue, só
exposto junto na leitura de GetAiPreferences para consulta única.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(memory): registra decisão dos dois stores de sessão de IA (issue #322)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* docs(architecture): cross-check gate React#200/epic #368 vs estado atual da API (#412)

6 dos 8 itens originais ja estao implementados (workspaces/me, pacote fiscal,
MappingDraft, contrato de saida TCL/XSL/XSLT, MappingExplanation, Test Lab
diff+cobertura), 1 esta parcial (OpenAPI gerado mas sem contract-check em CI)
e 1 revela gap pequeno nao previsto (capability como payload consultavel).

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* docs: fechar gate React#200/epic #368 — documentar 6 contratos já implementados (issue #413) (#419)

Cross-check de 2026-09-15 confirmou que 6 dos 8 itens da tabela original do gate #200
já estão implementados sem código novo: GET /api/workspaces/me, pacote fiscal
versionado, MappingDraft (FiscalProfile, editor manual de artefato, filtros de
release, arquivamento), contrato de saída TCL/XSL/XSLT (hash/status/imutabilidade),
MappingExplanation e o Fiscal Test Lab (diff por regra, diff release×release,
cobertura de obrigatórios). Documenta o gap real que sobra (capability Sysmiddle
como payload consultável, issue #415) sem tratá-lo como resolvido.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* feat(mapping-drafts): endpoint de listagem por workspace (issue #416) (#420)

Front reportou gap: Mapping Studio exigia digitar o GUID do draft manualmente,
sem forma de descobrir/listar. Adiciona GET .../mapping-drafts espelhando o
padrão de descoberta já usado em mapping-releases (issue #377): paginação,
RBAC de leitura (qualquer papel de membro) e filtro opcional por engine.
Draft não tem "status" próprio (só as regras têm), então não há filtro de
status neste endpoint.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* fix(deploy): portar migracao NSSM -> servico nativo para producao (#426)

deploy.yml nunca teve a logica de deteccao/migracao do servico legado
NSSM (C:\Users\Administrator\Downloads\nssm-2.24) que o ci-dev.yml ja
usa a cada deploy de dev - producao so fazia Stop-Service/Start-Service
genericos, sem checar o ImagePath do registro nem recriar como servico
Windows nativo (Program.cs usa UseWindowsService() ha tempo).

Porta a mesma logica do ci-dev.yml: no stop, checa se o ImagePath
contem "nssm" e, se sim, remove o registro (sc.exe delete); no restart,
recria o servico nativo (sc.exe create/description/failure) caso ele
nao exista - cobrindo tanto a migracao quanto o primeiro deploy num
host novo. Como sc.exe delete apaga a chave Environment que o step
"Configurar ambiente do servico" ja tinha escrito antes nesse mesmo
registro NSSM, captura o Environment antes do delete e restaura apos
criar o servico nativo, para nao perder Database__Password/
IdentityDatabase__*/etc. na primeira subida.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* feat(fiscal): endpoint de arvore de layout dupla origem/destino (issue #425) (#427)

Implementa GET /api/workspaces/{workspaceId}/mappings/{mappingId}/layout-tree
conforme o ADR "adr-layout-tree-endpoint-425": generaliza GuidXPathCatalog
(ai/XslSynth.Contracts) com extracao de cardinalidade (MinimalOccurrence/
MaximumOccurrence) e um novo BuildTree recursivo (achatando wrappers Choice/
Sequence como irmaos, mesma convencao do XPath ja existente), troca a fonte
de arquivo local por lookup via ICachedLayoutService/ICachedMapperService ja
registrados em producao, e expoe o contrato source/target/rules para o React
replicar a UI de dupla-arvore do Connect Us.

Regras[] reaproveitam apenas LinkMappingItemVO (unico ponto do MapperVO real
onde origem e destino sao GUIDs de no, nao path de DSL) - decisao explicita
do ADR pra nao inventar GUID de origem para as regras DSL (MapperRule).

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* fix(fiscal): alinhar identidade de nó entre layout-tree e explanation (#430) (#431)

* fix(fiscal): alinhar identidade de nó entre layout-tree e explanation (#430)

Problema 1: SysmiddleExplanationAdapter.ToExplainedRule priorizava
TargetLeafName (nome legível) sobre TargetGuid em SourceRefs/TargetRefs,
enquanto LayoutTreeService.ResolveRules sempre usa InputGuid/TargetGuid —
o cruzamento regra<->nó no front quebrava quando TargetLeafName estava
preenchido. Agora ambos usam sempre InputGuid/TargetGuid; o nome legível
continua disponível via HumanDescription.

Problema 2: regras DSL (MapperRule/branches condicionais) aparecem em
explanation.rules com sourceRefs/targetRefs prefixados I./T. (texto da
DSL, não GUID de nó), mas nunca entravam em layout-tree.rules[] -
LayoutTreeService.ResolveRules já documentava essa decisão ("inventar um
GUID aqui violaria nunca inventa"). Optei por (b): documentar
explicitamente via novo campo LayoutTreeResponse.Limitations, populado
quando o mapper tem regras DSL, em vez de (a) resolver os prefixos pra
GUID - o catálogo GuidXPathCatalog resolve por GUID, não por nome de
campo, e reconstruir esse contexto está fora do escopo desta issue.

Testes cobrindo os dois casos em MappingExplanationAdaptersTests e
LayoutTreeServiceTests; ajustado LayoutTreeControllerTests pro novo
parâmetro posicional.

* docs: documentar GET .../layout-tree e divergência de cobertura das rules (#425/#430)

Adiciona §8.3 bilíngue ao README com exemplo de request/response e nota
explícita sobre limitations[]: rules[] do layout-tree cobre só vínculo
direto campo->campo, diferente de explanation.rules[] que inclui regras
condicionais/DSL. XML docs em LayoutTree.cs e LayoutTreeController.cs já
estavam completos, sem alteração necessária.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
elson-vinicius-lopes added a commit that referenced this pull request Sep 17, 2026
* feat(xml-analysis): endpoint de reconstrução reversa + validação contra TXT original (issue #151) (#407)

* feat(xml-analysis): fecha issue #151 — endpoint de reconstrução reversa + validação contra TXT original

ReverseReconstructionService (best-effort XML->TXT) já existia mas não estava
plugado em nenhum controller/DI. Adiciona POST api/xml-analysis/reverse-reconstruct,
que recebe LayoutXml + o crosswalk FieldToXmlMapping[] já resolvido + TargetXml, e
devolve o TXT reconstruído.

Fecha o gap real da issue (item 3 do critério de aceite): quando OriginalTxt é
informado, ReverseReconstructionValidator compara campo a campo o valor escrito
(via XML) contra o valor real parseado do TXT original (mesmo ILayoutParserService
do pathway direto) — sem ele, a resposta não finge validação nenhuma.

ReconstructionResult ganha ReconstructedFields (issue #151) para dar a
granularidade por campo que a validação precisa, sem reparsear ReconstructedText.

Layout com WithBreakLines=false (MQSeries/IDOC) vira scopeWarning, não erro —
mantém o escopo MVP já declarado no serviço.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* chore(security): baseline do SCS0003 no reverse-reconstruct (issue #151)

O gate de CI (Security Code Scan - gate por severidade) bloqueou o PR #407 por
um achado novo: SCS0003 (XPath injection) em
Services/XmlAnalysis/ReverseReconstructionService.cs:158
(navigator.SelectSingleNode(target.Xpath)).

target.Xpath vem do corpo do POST api/xml-analysis/reverse-reconstruct, mas tanto
ele quanto o TargetXml contra o qual roda vem da MESMA request do MESMO chamador
autenticado - sem cenario de vazamento cross-documento. E leitura pura, ja dentro
de try/catch(XPathException). Aceito conscientemente como debito de baixo risco,
documentado no proprio finding.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* feat(ai): preferências de usuário além do prompt customizado (issue #322) (#409)

* feat(ai): preferências de usuário além do prompt customizado (issue #322)

Adiciona idioma de exibição, nível de detalhe da explicação e engine padrão
(TCL/XSLT) como preferências persistentes em tbLpAiUserSession (colunas novas
via ALTER idempotente), com upsert parcial (SetPreferencesAsync) e leitura com
defaults (GetPreferencesAsync). Endpoint dedicado PUT/GET api/transformation/
ai-preferences, [Authorize] + fail-closed 404 sem identidade, 422 para engine/
nível de detalhe fora dos valores aceitos.

O prompt customizado (ai-prompt-adicional, issue #98) continua no
AiUserInstructionStore em memória — não migrado para o SQL nesta issue, só
exposto junto na leitura de GetAiPreferences para consulta única.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(memory): registra decisão dos dois stores de sessão de IA (issue #322)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* docs(architecture): cross-check gate React#200/epic #368 vs estado atual da API (#412)

6 dos 8 itens originais ja estao implementados (workspaces/me, pacote fiscal,
MappingDraft, contrato de saida TCL/XSL/XSLT, MappingExplanation, Test Lab
diff+cobertura), 1 esta parcial (OpenAPI gerado mas sem contract-check em CI)
e 1 revela gap pequeno nao previsto (capability como payload consultavel).

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* docs: fechar gate React#200/epic #368 — documentar 6 contratos já implementados (issue #413) (#419)

Cross-check de 2026-09-15 confirmou que 6 dos 8 itens da tabela original do gate #200
já estão implementados sem código novo: GET /api/workspaces/me, pacote fiscal
versionado, MappingDraft (FiscalProfile, editor manual de artefato, filtros de
release, arquivamento), contrato de saída TCL/XSL/XSLT (hash/status/imutabilidade),
MappingExplanation e o Fiscal Test Lab (diff por regra, diff release×release,
cobertura de obrigatórios). Documenta o gap real que sobra (capability Sysmiddle
como payload consultável, issue #415) sem tratá-lo como resolvido.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* feat(mapping-drafts): endpoint de listagem por workspace (issue #416) (#420)

Front reportou gap: Mapping Studio exigia digitar o GUID do draft manualmente,
sem forma de descobrir/listar. Adiciona GET .../mapping-drafts espelhando o
padrão de descoberta já usado em mapping-releases (issue #377): paginação,
RBAC de leitura (qualquer papel de membro) e filtro opcional por engine.
Draft não tem "status" próprio (só as regras têm), então não há filtro de
status neste endpoint.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* fix(deploy): portar migracao NSSM -> servico nativo para producao (#426)

deploy.yml nunca teve a logica de deteccao/migracao do servico legado
NSSM (C:\Users\Administrator\Downloads\nssm-2.24) que o ci-dev.yml ja
usa a cada deploy de dev - producao so fazia Stop-Service/Start-Service
genericos, sem checar o ImagePath do registro nem recriar como servico
Windows nativo (Program.cs usa UseWindowsService() ha tempo).

Porta a mesma logica do ci-dev.yml: no stop, checa se o ImagePath
contem "nssm" e, se sim, remove o registro (sc.exe delete); no restart,
recria o servico nativo (sc.exe create/description/failure) caso ele
nao exista - cobrindo tanto a migracao quanto o primeiro deploy num
host novo. Como sc.exe delete apaga a chave Environment que o step
"Configurar ambiente do servico" ja tinha escrito antes nesse mesmo
registro NSSM, captura o Environment antes do delete e restaura apos
criar o servico nativo, para nao perder Database__Password/
IdentityDatabase__*/etc. na primeira subida.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* feat(fiscal): endpoint de arvore de layout dupla origem/destino (issue #425) (#427)

Implementa GET /api/workspaces/{workspaceId}/mappings/{mappingId}/layout-tree
conforme o ADR "adr-layout-tree-endpoint-425": generaliza GuidXPathCatalog
(ai/XslSynth.Contracts) com extracao de cardinalidade (MinimalOccurrence/
MaximumOccurrence) e um novo BuildTree recursivo (achatando wrappers Choice/
Sequence como irmaos, mesma convencao do XPath ja existente), troca a fonte
de arquivo local por lookup via ICachedLayoutService/ICachedMapperService ja
registrados em producao, e expoe o contrato source/target/rules para o React
replicar a UI de dupla-arvore do Connect Us.

Regras[] reaproveitam apenas LinkMappingItemVO (unico ponto do MapperVO real
onde origem e destino sao GUIDs de no, nao path de DSL) - decisao explicita
do ADR pra nao inventar GUID de origem para as regras DSL (MapperRule).

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* fix(fiscal): alinhar identidade de nó entre layout-tree e explanation (#430) (#431)

* fix(fiscal): alinhar identidade de nó entre layout-tree e explanation (#430)

Problema 1: SysmiddleExplanationAdapter.ToExplainedRule priorizava
TargetLeafName (nome legível) sobre TargetGuid em SourceRefs/TargetRefs,
enquanto LayoutTreeService.ResolveRules sempre usa InputGuid/TargetGuid —
o cruzamento regra<->nó no front quebrava quando TargetLeafName estava
preenchido. Agora ambos usam sempre InputGuid/TargetGuid; o nome legível
continua disponível via HumanDescription.

Problema 2: regras DSL (MapperRule/branches condicionais) aparecem em
explanation.rules com sourceRefs/targetRefs prefixados I./T. (texto da
DSL, não GUID de nó), mas nunca entravam em layout-tree.rules[] -
LayoutTreeService.ResolveRules já documentava essa decisão ("inventar um
GUID aqui violaria nunca inventa"). Optei por (b): documentar
explicitamente via novo campo LayoutTreeResponse.Limitations, populado
quando o mapper tem regras DSL, em vez de (a) resolver os prefixos pra
GUID - o catálogo GuidXPathCatalog resolve por GUID, não por nome de
campo, e reconstruir esse contexto está fora do escopo desta issue.

Testes cobrindo os dois casos em MappingExplanationAdaptersTests e
LayoutTreeServiceTests; ajustado LayoutTreeControllerTests pro novo
parâmetro posicional.

* docs: documentar GET .../layout-tree e divergência de cobertura das rules (#425/#430)

Adiciona §8.3 bilíngue ao README com exemplo de request/response e nota
explícita sobre limitations[]: rules[] do layout-tree cobre só vínculo
direto campo->campo, diferente de explanation.rules[] que inclui regras
condicionais/DSL. XML docs em LayoutTree.cs e LayoutTreeController.cs já
estavam completos, sem alteração necessária.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* fix(learning): corrigir split de linha MQSeries no aprendizado de layout (#434)

MQSeries e um stream continuo de largura fixa (600 chars) sem
terminador de linha entre "linhas" logicas. LayoutLearningService
fazia content.Split('\r','\n') incondicionalmente, tratando o
arquivo inteiro como 1 unica linha gigante -> TotalFields=0 (13 dos
14 mapeadores .mq_series em Examples/ afetados; evidencia real em
LAY_TXT_MQSERIES_ENVNFE_4.00_NFe/layout_learned.json, LineLength
35400 para um arquivo de 59 linhas de 600 chars).

Correcoes:
- ParseController: nao colapsa mais "mqseries" em "txt" ao chamar
  LearnFromFileAsync, preservando a distincao de formato.
- LayoutLearningService: para fileType="mqseries", usa o mesmo
  ILineSplitter/PositionalFormat.ContinuousStream do parse real em
  vez de split por \r\n, garantindo que aprendizado e parse
  concordem sobre onde as linhas comecam/terminam.
- Corrigido tambem um segundo bug exposto pelo primeiro: o
  agrupamento de linhas por prefixo literal (DetectLinePatterns)
  nunca junta 3+ linhas MQSeries porque cada linha comeca com um
  contador sequencial de 9 digitos unico por linha (nao um prefixo
  textual repetido como "LINHA001") -- mesmo com o split fisico
  correto, isso ainda dava TotalFields=0. Agrupamento dedicado
  (DetectContinuousStreamGroups) separa HEADER do restante.

Validado contra 4 arquivos .mq_series reais de clientes diferentes
(LAY_TXT_MQSERIES_ENVNFE_4.00_NFe, LAY_CNHI_..., LAY_IVECCO_...,
LAY_TXT_COMAU_...): TotalFields foi de 0 para 8/8/8/10 e TotalLines
passou a bater com tamanho_arquivo/600 em vez de sempre 1.

Testes novos: LayoutLearningServiceMqSeriesTests (fixture sintetico
de 600 chars/linha sem terminador, cobrindo split fisico, aprendizado
de campos reais, e regressao negativa de IDOC/record-per-line).

* Feat/xslt generation loop corpus neogrid (#435)

* feat(ai): primeira rodada real do loop RAG->XSLT no corpus Neogrid (caso InutNFe)

Reusa o loop ja existente em ai/XslSynth (DatasetFewShotIndex + metrics-batch,
RAG->Ollama->validacao) sobre 159 pares reais tcl/xsl indexados do corpus
Examples/{tcl,xsl}, avaliando o par NFe006c_InutNFe_NeoGridToSefaz com o
modelo fine-tuned layoutparser-sysmiddle-dsl:1.5b.

Resultado real (nao simulado): 10/11 campos de <infInut> saem byte-a-byte
identicos ao XSLT de producao (Id/tpAmb/xServ/cUF/ano/CNPJ/mod/serie/
nNFIni/nNFFin); xJust diverge (regra de negocio de prefixo+truncamento
nao inferivel do TCL); falta o wrapper estrutural (xsl:template, root
inutNFe+namespace+versao).

appsettings.Development.json ganha RAG:ExamplesPath/XsdValidation:BasePath
apontando pro corpus local de sessao (.claude/temp/, gitignored).

Persistencia via SqlMappingReleaseStore (padrao dbo.tbMappingRelease)
NAO implementada nesta rodada: sem credencial IdentityDatabase nesta
sessao para validar contra o schema real - documentado como bloqueio,
nao simulado. Detalhe completo na memoria do agente.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(memory): registrar validação real da persistência experimental RAG->XSLT

Round-trip real (INSERT+SELECT) via SqlMappingReleaseStore contra o
IdentityDatabase confirmado com credencial de dotnet user-secrets: ReleaseId
1d612a3c-8b30-4fcb-9fdf-dad1b085608b criado e relido com conteúdo/hash
idênticos. O teste de integração usado para validar foi removido antes do
commit (ci-dev.yml não injeta IdentityDatabase:* no step de dotnet test,
então o teste quebraria o gate de qualquer PR em vez de fazer skip
gracioso) — resultado documentado na memória do agente para não se perder.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* feat(fiscal): catalogo de exemplos de referencia TCL/XSL da Neogrid (#437)

Endpoint GET /api/reference-examples (+ /{id} para conteudo) expondo o
corpus real de transformacoes da Neogrid como material de referencia/
oraculo, deliberadamente fora do dominio MappingDraft/MappingRelease
(sem workspaceId, sem ArtifactSource) para nao misturar exemplo estatico
com governanca de release real.

ReferenceExampleCatalogService le disco via ReferenceExamples:BasePath
(configuravel, sem hardcode de path local), pareando arquivos .tcl/.xsl
por DocType/Versao/nome-base; degrada para lista vazia se o path nao
existir. Registrado em Program.cs no grupo Fiscal.

Testes cobrem: listagem com fixture sintetica (par completo, tcl sem
par, filtro por docType), leitura de conteudo por id, e degradacao
graciosa com BasePath vazio/inexistente.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant