From 34831c9c4b212e894b28105eee918666485fe9b1 Mon Sep 17 00:00:00 2001 From: Elson Lopes Date: Sun, 23 Aug 2026 19:01:21 -0300 Subject: [PATCH 1/5] fix(bff): logar errorMessage truncado nas falhas OIDC Entra/Google Adiciona a mensagem de erro (truncada em 500 chars) aos logs estruturados de falha nas etapas de metadata/token exchange dos fluxos Entra e Google, para dar visibilidade a uma investigacao em aberto de login intermitente (falha ~10.6s, causa raiz suspeita de DNS lento no host de producao). Nao altera comportamento de retry nem resposta ao usuario. --- server/src/oidc.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/server/src/oidc.ts b/server/src/oidc.ts index d2c05ad..b69e359 100644 --- a/server/src/oidc.ts +++ b/server/src/oidc.ts @@ -230,6 +230,12 @@ class MsalOidcClient implements OidcClient { durationMs: Date.now() - startedAt, errorKind: classifyErrorKind(error), errorType: error instanceof Error ? error.name : 'UnknownError', + // Diagnóstico: nesta etapa (metadata da autoridade Entra) o MSAL não recebe nenhum dado + // de usuário (code/state/token) — os parâmetros enviados são só client id/secret e + // escopos fixos. A mensagem de erro do MSAL (ex.: "fetch failed", timeout) não carrega + // esses segredos, então é seguro logar truncada para correlacionar rede lenta vs. outra + // causa na próxima ocorrência real. + errorMessage: error instanceof Error ? error.message.slice(0, 500) : undefined, }, 'Falha ao obter a URL de autorização do Entra (possível metadata de autoridade fria).' ); @@ -272,6 +278,14 @@ class MsalOidcClient implements OidcClient { durationMs: Date.now() - startedAt, errorKind: classifyErrorKind(error), errorType: error instanceof Error ? error.name : 'UnknownError', + // Diagnóstico (achado nesta investigação): o MSAL embrulha falha de rede em + // NetworkError, mas o nome/mensagem do erro original de rede (ex.: "fetch failed", + // timeout) só existe dentro de `error.message` — nunca em `.cause`/`.code` — então + // `classifyErrorKind` sozinho não distingue rede lenta de erro de validação de token. + // A mensagem do MSAL aqui é sempre sobre a chamada HTTP ao endpoint de token ou sobre + // validação de claims/assinatura; não inclui `code`/`state`/tokens em texto (o MSAL não + // ecoa esses valores nas mensagens de erro que produz). Truncado como precaução. + errorMessage: error instanceof Error ? error.message.slice(0, 500) : undefined, }, 'Falha ao trocar o código de autorização Entra por um token.' ); @@ -340,6 +354,9 @@ class GoogleOidcClient implements OidcClient { durationMs: Date.now() - startedAt, errorKind: classifyErrorKind(error), errorType: error instanceof Error ? error.name : 'UnknownError', + // Mesma lacuna de observabilidade do fluxo Entra: só `client id`/`secret` fixos + // trafegam nesta etapa (metadata OIDC do Google), sem dado de usuário na mensagem. + errorMessage: error instanceof Error ? error.message.slice(0, 500) : undefined, }, 'Descoberta OIDC do Google falhou; esta promise fica cacheada e todo login Google ' + 'seguinte reusará esta mesma falha até o processo do BFF reiniciar.' @@ -406,6 +423,10 @@ class GoogleOidcClient implements OidcClient { durationMs: Date.now() - startedAt, errorKind: classifyErrorKind(error), errorType: error instanceof Error ? error.name : 'UnknownError', + // `openid-client` reporta erros do grant (rede, resposta OAuth de erro, validação de + // claims) em `error.message`; não ecoa `code`/`codeVerifier`/tokens em texto na + // mensagem. Truncado como precaução, mesmo padrão do fluxo Entra acima. + errorMessage: error instanceof Error ? error.message.slice(0, 500) : undefined, }, 'Falha ao trocar o código de autorização Google por um token.' ); From e209588625bbddd266a87818e67593feac18e04c Mon Sep 17 00:00:00 2001 From: Elson Lopes Date: Sun, 23 Aug 2026 19:01:27 -0300 Subject: [PATCH 2/5] fix(ui): diferenciar candidatos Sysmiddle por candidateId Quando ha multiplos candidatos de transformacao do mesmo pathway (ex.: dois "Sysmiddle"), as abas agora mostram os 8 primeiros chars do GUID embutido no candidateId, ja que o contrato atual nao expoe nome de mapper nem layoutOutputTarget. Limitacao de contrato documentada no componente. --- .../analysis/XmlTransformationDisplay.css | 9 +++ .../analysis/XmlTransformationDisplay.tsx | 68 ++++++++++++++----- 2 files changed, 60 insertions(+), 17 deletions(-) diff --git a/src/components/analysis/XmlTransformationDisplay.css b/src/components/analysis/XmlTransformationDisplay.css index a50c821..866720c 100644 --- a/src/components/analysis/XmlTransformationDisplay.css +++ b/src/components/analysis/XmlTransformationDisplay.css @@ -256,6 +256,15 @@ box-shadow: var(--shadow-sm); } +/* Diferenciador entre candidatos do mesmo pathway (ex.: dois "Sysmiddle") — só o recorte do + candidateId disponível hoje no contrato; ver comentário em XmlTransformationDisplay.tsx. */ +.xml-transformation-candidate-btn__id { + opacity: 0.75; + font-weight: var(--font-weight-regular, 400); + font-family: var(--font-family-mono, monospace); + font-size: 0.9em; +} + .xml-transformation-delivery-actions { display: flex; flex-wrap: wrap; diff --git a/src/components/analysis/XmlTransformationDisplay.tsx b/src/components/analysis/XmlTransformationDisplay.tsx index 6418995..b30d632 100644 --- a/src/components/analysis/XmlTransformationDisplay.tsx +++ b/src/components/analysis/XmlTransformationDisplay.tsx @@ -20,6 +20,26 @@ const PATHWAY_LABEL: Record = { 'tcl-xsl': 'TCL/XSL', }; +/** + * Diferenciador visual entre candidatos do MESMO pathway (ex.: dois candidatos "sysmiddle"). + * + * ⚠️ Bloqueio de contrato: `TransformationCandidate` (src/types/transformation.ts) não expõe + * nome amigável do Mapeador nem `layoutOutputTarget` — só `candidateId`, que por convenção do + * back-end (ver doc do tipo) é "sysmiddle-{MapperGuid}" ou "tclxsl-1". Sem esses campos no + * contrato, o máximo que dá pra mostrar sem inventar dado é um recorte curto do próprio + * `candidateId` (o GUID do mapper), só para o usuário distinguir as abas — não é um nome + * legível. Pedir à API `mapperName`/`layoutOutputTarget` em `execute-candidates` resolveria de + * verdade; até lá, este é o diferenciador possível. + */ +const buildCandidateDifferentiator = (candidateId: string, pathway: string): string | null => { + const prefix = `${pathway}-`; + const suffix = candidateId.startsWith(prefix) ? candidateId.slice(prefix.length) : candidateId; + const trimmed = suffix.trim(); + if (!trimmed || trimmed === candidateId) return null; + // GUID de mapper: mostrar só os 8 primeiros caracteres para não estourar o botão. + return trimmed.length > 8 ? `${trimmed.slice(0, 8)}…` : trimmed; +}; + interface XmlDeliveryFeedback { kind: 'success' | 'error'; message: string; @@ -455,23 +475,37 @@ const XmlTransformationDisplay: React.FC = () => { role="tablist" aria-label="Candidatos de transformação" > - {candidates.map(candidate => ( - - ))} + {candidates.map(candidate => { + const pathwayLabel = PATHWAY_LABEL[candidate.pathway] || candidate.pathway; + const differentiator = buildCandidateDifferentiator( + candidate.candidateId, + candidate.pathway + ); + return ( + + ); + })} )} From 27f256859659f598a4f7f9fcee8915cf567e05e4 Mon Sep 17 00:00:00 2001 From: Elson Lopes Date: Sun, 23 Aug 2026 19:01:31 -0300 Subject: [PATCH 3/5] fix(devops): forcar IPv4 e corrigir extracao da URL do tunnel Cloudflare Adiciona --edge-ip-version 4 ao cloudflared: neste host a resolucao de edge via IPv6 trava ate timeout no POST inicial de "Requesting new quick Tunnel". Corrige tambem a extracao da URL publica do log, que podia capturar api.trycloudflare.com (endpoint interno) ou uma tentativa antiga de uma execucao anterior; agora busca a partir do ultimo marcador de inicio do launcher e pega o match mais recente. --- scripts/Register-CloudflareTunnel.ps1 | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/scripts/Register-CloudflareTunnel.ps1 b/scripts/Register-CloudflareTunnel.ps1 index a5e1811..0c3293d 100644 --- a/scripts/Register-CloudflareTunnel.ps1 +++ b/scripts/Register-CloudflareTunnel.ps1 @@ -77,8 +77,12 @@ $launcher = @( '$startTimestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"', "`$cloudflaredExe = $(ConvertTo-PowerShellLiteral $CloudflaredPath)", "`$publicHost = $(ConvertTo-PowerShellLiteral $PublicHost)", + '# --edge-ip-version 4: neste host, resolução de edge via IPv6 trava até timeout no POST' + + ' inicial de "Requesting new quick Tunnel" (rota IPv6 de saída indisponível/bloqueada);' + + ' IPv4 funciona normalmente. Ver diagnóstico em' + + ' project_cloudflare_tunnel_task_never_registered_2026_08_22.md.', '$cloudflaredArgs = "tunnel --url https://127.0.0.1:443 --protocol http2 --no-tls-verify " +' + - '"--origin-server-name `"" + $publicHost + "`" --http-host-header `"" + $publicHost + "`""', + '"--edge-ip-version 4 --origin-server-name `"" + $publicHost + "`" --http-host-header `"" + $publicHost + "`""', '$cmdLine = "`"" + $cloudflaredExe + "`" " + $cloudflaredArgs + " >> `"" + $logFile + "`" 2>&1"', 'Add-Content -LiteralPath $logFile -Value "[$startTimestamp] --- launcher iniciando cloudflared ' + '(boot da task ou reinício automático via RestartCount) ---"', @@ -116,8 +120,20 @@ Write-Host "Aguardando o cloudflared publicar a URL pública no log ($logFile).. Start-Sleep -Seconds 8 if (Test-Path -LiteralPath $logFile) { - $urlMatch = Select-String -LiteralPath $logFile -Pattern 'https://[a-z0-9-]+\.trycloudflare\.com' | - Select-Object -First 1 + # O log é append-only entre reinícios/execuções; "api.trycloudflare.com" é o endpoint da + # API do Cloudflare (usado internamente pelo cloudflared para requisitar o túnel), não a + # URL pública — precisa ser excluído do match, senão uma tentativa antiga/falha nas + # primeiras linhas do arquivo é capturada em vez da URL real. Também restringimos a busca + # às linhas a partir do ÚLTIMO marcador "--- launcher iniciando cloudflared ---" e pegamos + # o match mais recente (Select-Object -Last 1), para refletir a execução atual do processo. + $allLines = Get-Content -LiteralPath $logFile + $lastStartIndex = ($allLines | Select-String -Pattern '--- launcher iniciando cloudflared ---' | + Select-Object -Last 1).LineNumber + $relevantLines = if ($lastStartIndex) { $allLines[($lastStartIndex - 1)..($allLines.Count - 1)] } else { $allLines } + $urlMatch = $relevantLines | + Select-String -Pattern 'https://[a-z0-9-]+\.trycloudflare\.com' | + Where-Object { $_.Matches[0].Value -ne 'https://api.trycloudflare.com' } | + Select-Object -Last 1 if ($urlMatch) { Write-Host "URL pública do túnel: $($urlMatch.Matches[0].Value)" Write-Host 'Cadastre essa URL como BFF_PUBLIC_ORIGIN / vars.PUBLIC_HOST e nos redirect URIs' ` From c98e6488603e722ae4b6d52f40c8c5096538ac14 Mon Sep 17 00:00:00 2001 From: Elson Lopes Date: Sun, 23 Aug 2026 19:01:34 -0300 Subject: [PATCH 4/5] docs(agents): atualizar memoria de agentes Atualiza e adiciona registros de memoria de lp-devops, lp-front-dev e lp-product-manager cobrindo o trabalho desta sessao (logs OIDC, tunnel Cloudflare, diferenciador de candidatos, investigacoes em aberto). --- .claude/agent-memory/lp-devops/MEMORY.md | 5 +- ...sistent_logs_and_cloudflare_tunnel_task.md | 17 +++++- ...f_public_origin_manual_patch_2026_08_23.md | 29 ++++++++++ ...tunnel_task_never_registered_2026_08_22.md | 44 +++++++++++++++ ...udflare_tunnel_url_extraction_regex_bug.md | 38 +++++++++++++ ..._task_and_login_flakiness_investigation.md | 17 ++++-- .claude/agent-memory/lp-front-dev/MEMORY.md | 4 ++ ...nal_edit_multi_occurrence_investigation.md | 54 +++++++++++++++++++ ...project_transformation_candidate_id_gap.md | 24 +++++++++ .../lp-product-manager/product-governance.md | 14 +++++ 10 files changed, 239 insertions(+), 7 deletions(-) create mode 100644 .claude/agent-memory/lp-devops/project_bff_public_origin_manual_patch_2026_08_23.md create mode 100644 .claude/agent-memory/lp-devops/project_cloudflare_tunnel_task_never_registered_2026_08_22.md create mode 100644 .claude/agent-memory/lp-devops/project_cloudflare_tunnel_url_extraction_regex_bug.md create mode 100644 .claude/agent-memory/lp-front-dev/project_positional_edit_multi_occurrence_investigation.md create mode 100644 .claude/agent-memory/lp-front-dev/project_transformation_candidate_id_gap.md diff --git a/.claude/agent-memory/lp-devops/MEMORY.md b/.claude/agent-memory/lp-devops/MEMORY.md index c415122..785c792 100644 --- a/.claude/agent-memory/lp-devops/MEMORY.md +++ b/.claude/agent-memory/lp-devops/MEMORY.md @@ -13,8 +13,11 @@ - [Páginas legais com placeholder](project_legal_pages_placeholders_pending.md) — `/terms` e `/privacy` esperam e-mail/jurisdição da empresa; não preencher sozinho. - [Plano Cloudflare Quick Tunnel p/ OAuth](project_cloudflare_quick_tunnel_google_oauth_plan.md) — checklist não executado; BFF prod escuta 127.0.0.1:3100. - [.env do BFF é fail-fast](project_bff_env_failfast_placeholders.md) — placeholder no valor aborta o boot; use comentário e chave vazia. -- [Log persistente do BFF + tunnel Cloudflare](project_bff_persistent_logs_and_cloudflare_tunnel_task.md) — `logs/` fora do release; task própria e idempotente para o túnel. +- [Log persistente do BFF + script do tunnel Cloudflare](project_bff_persistent_logs_and_cloudflare_tunnel_task.md) — `logs/` fora do release; script idempotente escrito, NUNCA registrado em produção (ver correção 2026-08-22). - [VirtualBox autostart + investigação de login flaky](project_virtualbox_autostart_task_and_login_flakiness_investigation.md) — task S4U/`-RunAsUser` (não SYSTEM); bug de discovery cacheado no GoogleOidcClient; hipótese MSAL cold-start não confirmada. +- [Cloudflare tunnel NUNCA foi registrado (2026-08-22)](project_cloudflare_tunnel_task_never_registered_2026_08_22.md) — evidência real do host: task/processo/log ausentes; URL do Entra veio de run manual que morreu; registrado com sucesso no mesmo dia com fix `--edge-ip-version 4`. +- [Bug de extração de URL do tunnel](project_cloudflare_tunnel_url_extraction_regex_bug.md) — regex pegava `api.trycloudflare.com` (endpoint interno) por log append-only + `-First 1`; corrigido para ancorar no último marcador de boot + excluir host da API. +- [Patch manual BFF_PUBLIC_ORIGIN (2026-08-23)](project_bff_public_origin_manual_patch_2026_08_23.md) — Start-Bff.ps1 do release editado à mão p/ apontar pro Quick Tunnel; some no próximo deploy. Arquitetura vigente: front same-origin; IIS HTTPS anônimo encaminha `/auth` e `/api` → BFF Node em loopback com Entra OIDC/sessão criptografada → API .NET. `Deploy-Iis.ps1` desabilita Windows diff --git a/.claude/agent-memory/lp-devops/project_bff_persistent_logs_and_cloudflare_tunnel_task.md b/.claude/agent-memory/lp-devops/project_bff_persistent_logs_and_cloudflare_tunnel_task.md index 48543d3..3666e89 100644 --- a/.claude/agent-memory/lp-devops/project_bff_persistent_logs_and_cloudflare_tunnel_task.md +++ b/.claude/agent-memory/lp-devops/project_bff_persistent_logs_and_cloudflare_tunnel_task.md @@ -1,10 +1,25 @@ --- name: project_bff_persistent_logs_and_cloudflare_tunnel_task -description: BFF launcher now logs to a persistent file; Cloudflare Quick Tunnel got its own idempotent Scheduled Task, decoupled from normal deploys. +description: BFF launcher now logs to a persistent file; Cloudflare Quick Tunnel got its own idempotent Scheduled Task SCRIPT — written but NOT yet run on the production host (see 2026-08-22 correction below). metadata: type: project --- +**CORREÇÃO (2026-08-22):** evidência coletada pelo usuário no host de produção mostrou que a +Scheduled Task `Cloudflared-QuickTunnel` **nunca foi registrada de fato**. `Get-ScheduledTask` +só retorna `LayoutParserFrontend-BFF`; `Get-Process -Name cloudflared` está vazio; +`cloudflared-tunnel.log` não existe em lugar nenhum de `C:\`. O binário +`C:\Program Files (x86)\cloudflared\cloudflared.exe` está instalado. Ou seja: o texto abaixo +descreve corretamente o *script* (`scripts/Register-CloudflareTunnel.ps1`, revisado de novo em +2026-08-19, ver [[project_virtualbox_autostart_task_and_login_flakiness_investigation]]), mas a +frase "registers ... as its own Scheduled Task" describe a intenção do script, não um fato já +aplicado em produção. A URL pública que ficou cadastrada como redirect URI no Entra +(`inspections-martha-excel-capability.trycloudflare.com`) veio de uma execução manual e +interativa de `cloudflared tunnel --url ...` em algum momento passado — quando essa sessão +fechou, o processo morreu e nunca voltou, o que explica o login quebrado relatado pelo usuário. +Antes de reusar qualquer afirmação deste arquivo como "já está em produção", confirme com +`Get-ScheduledTask -TaskName Cloudflared-QuickTunnel` no host. + Implemented on branch `feat/bff-logs-and-persistent-tunnel` (from `origin/develop`), 2026-08-15. diff --git a/.claude/agent-memory/lp-devops/project_bff_public_origin_manual_patch_2026_08_23.md b/.claude/agent-memory/lp-devops/project_bff_public_origin_manual_patch_2026_08_23.md new file mode 100644 index 0000000..9c450fa --- /dev/null +++ b/.claude/agent-memory/lp-devops/project_bff_public_origin_manual_patch_2026_08_23.md @@ -0,0 +1,29 @@ +--- +name: project_bff_public_origin_manual_patch_2026_08_23 +description: Patch manual e temporário de BFF_PUBLIC_ORIGIN no Start-Bff.ps1 do release ativo, aplicado em 2026-08-23 para destravar login via Cloudflare Quick Tunnel. +metadata: + type: project +--- + +Em 2026-08-23, com aprovação explícita do usuário, foram montados (não executados por mim — +entregues como comandos prontos) os passos para editar manualmente `Start-Bff.ps1` do release +em produção, trocando a linha `$env:BFF_PUBLIC_ORIGIN = '...'` de `https://BRNDDAPPBLD01` +(binding IIS) para `https://toll-packages-bell-squad.trycloudflare.com` (URL do Quick Tunnel +ativa naquele momento), seguido de restart isolado da Scheduled Task `LayoutParserFrontend-BFF` +(sem tocar no site IIS nem na task do Cloudflare). + +**Por que:** `BFF_PUBLIC_ORIGIN` é gerado a cada deploy por `Deploy-Iis.ps1:303` a partir da +variável `PUBLIC_HOST` do workflow (ainda `BRNDDAPPBLD01`), mas o login OIDC via Entra depende +do redirect URI bater com a origin pública real, que hoje é o hostname do Cloudflare Quick +Tunnel (efêmero — muda a cada novo tunnel registrado). Ver +[[project_cloudflare_tunnel_task_never_registered_2026_08_22]] e +[[project_cloudflare_tunnel_url_extraction_regex_bug]] para o histórico do tunnel. + +**Como aplicar:** este patch é MANUAL e TEMPORÁRIO — some no próximo deploy normal (`git push` +→ pipeline → `Deploy-Iis.ps1` regenera `Start-Bff.ps1` do zero). Não tratar esse valor como +definitivo. A correção real (discutida, não implementada) é desacoplar `BFF_PUBLIC_ORIGIN` do +`PUBLIC_HOST`/binding IIS — por exemplo, lendo a origin pública dinamicamente do output do +Register-CloudflareTunnel em vez de hardcodar no deploy. Antes de assumir que o login está +saudável em produção, confirme se este patch manual ainda está de pé (release pode ter mudado +com novo deploy) e se a URL do Quick Tunnel ainda é a mesma (ela muda se o tunnel foi +re-registrado). diff --git a/.claude/agent-memory/lp-devops/project_cloudflare_tunnel_task_never_registered_2026_08_22.md b/.claude/agent-memory/lp-devops/project_cloudflare_tunnel_task_never_registered_2026_08_22.md new file mode 100644 index 0000000..728a349 --- /dev/null +++ b/.claude/agent-memory/lp-devops/project_cloudflare_tunnel_task_never_registered_2026_08_22.md @@ -0,0 +1,44 @@ +--- +name: project_cloudflare_tunnel_task_never_registered_2026_08_22 +description: Evidência de 2026-08-22 confirmando que a Scheduled Task Cloudflared-QuickTunnel nunca foi registrada em produção, apesar de memórias anteriores sugerirem o contrário. +metadata: + type: project +--- + +Evidência coletada pelo usuário via PowerShell no host de produção, em 2026-08-22: + +- `Get-ScheduledTask | Where-Object { $_.TaskName -match 'loud|unnel|BFF' }` só retorna + `LayoutParserFrontend-BFF` (Running). Nenhuma task `Cloudflared-QuickTunnel`. +- `Get-Process -Name cloudflared` vazio — nenhum `cloudflared.exe` rodando. +- `Test-Path 'C:\Program Files (x86)\cloudflared\cloudflared.exe'` = True — binário instalado. +- Busca recursiva em `C:\` por `cloudflared-tunnel.log` não encontrou nada. + +**Conclusão:** o script `scripts/Register-CloudflareTunnel.ps1` sempre esteve correto (revisado +em 2026-08-15 e novamente em 2026-08-19), mas nunca foi de fato executado/registrado no host de +produção. A URL pública cadastrada como redirect URI no Entra +(`inspections-martha-excel-capability.trycloudflare.com`) veio de uma execução manual e +interativa de `cloudflared tunnel --url ...` em algum momento passado; quando a sessão +interativa fechou, o processo cloudflared morreu e nunca voltou — o que explica o login +quebrado relatado pelo usuário (o BFF em si segue de pé via `LayoutParserFrontend-BFF`, só o +túnel público caiu). + +**Por que:** memórias anteriores ([[project_bff_persistent_logs_and_cloudflare_tunnel_task]], +[[project_virtualbox_autostart_task_and_login_flakiness_investigation]]) descreviam a +implementação do *script* em termos que soavam como "já registrado em produção" — eram +descrições da lógica do script, não confirmação de execução real no host. Já foram corrigidas +com uma nota apontando para este arquivo. + +**Como aplicar:** antes de tratar a Cloudflare Quick Tunnel como ativa em produção, sempre +confirme com `Get-ScheduledTask -TaskName Cloudflared-QuickTunnel` no host — nunca assuma pela +memória. Registrar a task exige `-DeployRoot` (mesmo valor do secret `DEPLOY_PATH` usado por +`Deploy-Iis.ps1` via `.github/workflows/deploy.yml`) e, opcionalmente, `-PublicHost` (mesmo +valor da variável `PUBLIC_HOST`, usada para SNI/host-header do túnel — combina com o binding +HTTPS do site IIS, não é o hostname `*.trycloudflare.com`). Nenhum desses dois valores está +hardcoded neste repo; pergunte ao usuário ou leia os secrets/vars do environment `production` +no GitHub antes de montar o comando de registro. Execução é ação de produção — precisa +aprovação explícita do usuário, nunca automática. + +**Atualização 2026-08-22 (mesmo dia, após registro bem-sucedido):** a task foi registrada com +sucesso neste host (BRNDDAPPBLD01) com o fix `--edge-ip-version 4` (IPv6 travava o POST inicial +de "Requesting new quick Tunnel"; ver [[project_cloudflare_tunnel_url_extraction_regex_bug]] +para o bug de extração de URL encontrado logo em seguida, na mesma sessão de deploy). diff --git a/.claude/agent-memory/lp-devops/project_cloudflare_tunnel_url_extraction_regex_bug.md b/.claude/agent-memory/lp-devops/project_cloudflare_tunnel_url_extraction_regex_bug.md new file mode 100644 index 0000000..d59b79d --- /dev/null +++ b/.claude/agent-memory/lp-devops/project_cloudflare_tunnel_url_extraction_regex_bug.md @@ -0,0 +1,38 @@ +--- +name: project_cloudflare_tunnel_url_extraction_regex_bug +description: Bug de produção em Register-CloudflareTunnel.ps1 — regex capturava api.trycloudflare.com (endpoint interno) em vez da URL pública real do túnel; corrigido em 2026-08-22. +metadata: + type: project +--- + +Em 2026-08-22, após a task `Cloudflared-QuickTunnel` finalmente registrar e conectar com +sucesso no host de produção (ver [[project_cloudflare_tunnel_task_never_registered_2026_08_22]] +para o fix de IPv6 que resolveu o travamento anterior), o script imprimiu como resultado +`URL pública do túnel: https://api.trycloudflare.com` — que está errado. Esse host é o +endpoint da API do Cloudflare usado internamente pelo `cloudflared` para requisitar o túnel +(aparece em `Post "https://api.trycloudflare.com/tunnel"`), não a URL pública do túnel (que +tem formato `https://palavras-aleatorias.trycloudflare.com`). + +**Causa raiz:** o regex original `'https://[a-z0-9-]+\.trycloudflare\.com'` também confere com +"api". Como `logs/cloudflared-tunnel.log` é append-only entre reinícios/execuções, execuções +anteriores (as tentativas falhas de antes do fix de IPv6) deixaram no log a linha de erro +contendo `https://api.trycloudflare.com`. O script usava `Select-Object -First 1` no arquivo +inteiro, então pegou esse match antigo/errado em vez da URL real, que estava mais adiante +(gerada pela execução bem-sucedida atual). + +**Correção aplicada** em `scripts/Register-CloudflareTunnel.ps1` (bloco final, era linha +~122-136): (1) restringe a busca às linhas a partir do índice do **último** marcador +`--- launcher iniciando cloudflared ---` no log, ignorando execuções/reinícios anteriores +acumulados no arquivo; (2) filtra explicitamente fora o valor exato +`https://api.trycloudflare.com`; (3) usa `Select-Object -Last 1` dentro do trecho relevante +em vez de `-First 1` no arquivo inteiro. + +**Por que:** log append-only + regex genérico demais + pegar o primeiro match do arquivo +inteiro é uma combinação que sempre vai preferir ruído histórico a estado atual. Qualquer +outro script que faça parsing de log cumulativo neste projeto (ex.: BFF) deve preferir +"mais recente após o último marcador de boot" a "primeiro match do arquivo". + +**Como aplicar:** ao revisar/editar scripts que extraem informação de logs append-only, +sempre considerar se o valor procurado pode ter aparecido em execuções anteriores (falhas ou +não) antes da execução atual, e ancorar a busca num marcador de início de execução em vez de +depender só de `-First`/`-Last` sobre o arquivo inteiro. diff --git a/.claude/agent-memory/lp-devops/project_virtualbox_autostart_task_and_login_flakiness_investigation.md b/.claude/agent-memory/lp-devops/project_virtualbox_autostart_task_and_login_flakiness_investigation.md index 7ab9dfa..85970be 100644 --- a/.claude/agent-memory/lp-devops/project_virtualbox_autostart_task_and_login_flakiness_investigation.md +++ b/.claude/agent-memory/lp-devops/project_virtualbox_autostart_task_and_login_flakiness_investigation.md @@ -9,11 +9,18 @@ Requested 2026-08-19 on branch `feat/ia-fallback-polling` (not a dedicated infra did not ask to switch, so the new script was added in place; confirm before it's committed). **Cloudflare Quick Tunnel restart robustness (ask #1):** reviewed -`scripts/Register-CloudflareTunnel.ps1` — already correct for the "host reboots, nothing works -until someone opens PowerShell" problem: Scheduled Task `Cloudflared-QuickTunnel`, `SYSTEM` -principal, `-AtStartup` trigger, `RestartCount 999`/`RestartInterval 1min`, log rotation to -`logs/cloudflared-tunnel.log`. No domain purchased yet (confirmed again by user), so Quick -Tunnel stays; no code change made here. See [[project_bff_persistent_logs_and_cloudflare_tunnel_task]]. +`scripts/Register-CloudflareTunnel.ps1` — the *script* is already correct for the "host reboots, +nothing works until someone opens PowerShell" problem: Scheduled Task `Cloudflared-QuickTunnel`, +`SYSTEM` principal, `-AtStartup` trigger, `RestartCount 999`/`RestartInterval 1min`, log rotation +to `logs/cloudflared-tunnel.log`. No domain purchased yet (confirmed again by user), so Quick +Tunnel stays; no code change made here. **CORRECTION (2026-08-22): this review only verified the +script's logic — it was never confirmed that the task was actually registered on the production +host.** Evidence gathered 2026-08-22 shows it was NOT: no `Cloudflared-QuickTunnel` Scheduled +Task, no `cloudflared.exe` process running, no `cloudflared-tunnel.log` anywhere on `C:\`. The +Entra-registered redirect URI (`inspections-martha-excel-capability.trycloudflare.com`) came +from a one-off interactive `cloudflared tunnel` run that died when its session closed. See +[[project_bff_persistent_logs_and_cloudflare_tunnel_task]] for the full correction — verify with +`Get-ScheduledTask -TaskName Cloudflared-QuickTunnel` before assuming this is live. **New: `scripts/Register-VirtualBoxAutostart.ps1` (ask #2)** — registers a second, independent Scheduled Task (`VirtualBox-Autostart` by default) that runs diff --git a/.claude/agent-memory/lp-front-dev/MEMORY.md b/.claude/agent-memory/lp-front-dev/MEMORY.md index 5618048..a63b4a2 100644 --- a/.claude/agent-memory/lp-front-dev/MEMORY.md +++ b/.claude/agent-memory/lp-front-dev/MEMORY.md @@ -18,6 +18,10 @@ `feat/xml-navigable-tree`; PBI #128 (vínculo TXT↔XML) segue bloqueado por contrato. - [Lint de setState em effect](feedback_effect_setstate_lint.md) — `react-hooks/set-state-in-effect` é erro aqui; usar padrão "ajustar estado durante o render" em vez de `useEffect`. +- [Investigação edição posicional multi-ocorrência](project_positional_edit_multi_occurrence_investigation.md) — + hipótese não confirmada (dado de contrato); precisa do JSON real de `parseResult.fields` antes de mexer em `positionalFieldEdit.ts`. +- [Candidatos de transformação sem diferenciador](project_transformation_candidate_id_gap.md) — + `TransformationCandidate` não tem nome de mapper/`layoutOutputTarget`; só `candidateId`. Regras duráveis: HTTP só em `services/`; tipos em `src/types`; sem `any` novo; preserve `X-Correlation-ID`; payload TXT/XML não vai para logs/cache; produção nunca usa API absoluta. diff --git a/.claude/agent-memory/lp-front-dev/project_positional_edit_multi_occurrence_investigation.md b/.claude/agent-memory/lp-front-dev/project_positional_edit_multi_occurrence_investigation.md new file mode 100644 index 0000000..8e35c59 --- /dev/null +++ b/.claude/agent-memory/lp-front-dev/project_positional_edit_multi_occurrence_investigation.md @@ -0,0 +1,54 @@ +--- +name: positional-edit-multi-occurrence-investigation +description: Investigação (sem repro confirmada) de edição posicional concatenando ocorrências de uma mesma linha; mapa de onde olhar com dado real +metadata: + type: project +--- + +Usuário reportou (2026-08-23): em `FieldDisplay`/`FieldEditor`, uma linha com múltiplas +ocorrências (ex. LINHA081, 4 ocorrências) aparece corretamente separada ("Ocorrência N") no +modo leitura, mas ao clicar para editar um campo dela, o valor inicial do `` do +`FieldEditor` mostrava o texto de TODAS as ocorrências concatenado sem separador. + +Investigação feita (sem acesso ao payload real / TXT do usuário): + +- `src/components/analysis/FieldDisplay.tsx`: o modo leitura NUNCA lê `txtContent` bruto para + montar o texto de cada campo — usa sempre `field.value` (do JSON da API), padded/truncado + para `field.length`. Isso está confirmado correto e é o motivo de o modo leitura "esconder" + problemas de `length` mal calculado (trailing padding não aparece visualmente). +- `src/utils/positionalFieldEdit.ts` (`inspectPositionalField`): a edição, ao contrário, faz + fail-closed slicing direto do `txtContent` bruto usando + `lineIndex*600 + (startPosition-1)` até `+expectedLength` (`field.length`), e SÓ libera + edição se esse slice bater exatamente com `field.value` padded. Matematicamente + `currentValue.length === expectedLength` sempre (é um `.slice()`), então o `` não + pode mostrar mais caracteres que `field.length` — qualquer garbled/concatenação observada + não pode vir de overflow no componente em si. +- `src/utils/positionalFieldEdit.ts` (`resolvePositionalLineIndex`): para `lineSequence` de 3 + dígitos (o caso comum, linha identificada por número tipo "081"), resolve o índice físico da + ocorrência N contando quantos blocos físicos de 600 chars têm esse marcador na posição 6-9 e + pegando o N-ésimo. Isso pressupõe que cada ocorrência é um BLOCO FÍSICO SEPARADO de 600 + chars. Se, no documento real, as 4 ocorrências dessa linha estiverem TODAS dentro do MESMO + bloco físico de 600 (ex. grupo repetitivo compactado numa única linha, sem re-emitir + sequencial+número de linha por ocorrência), essa função fica com `matches.length === 1` e + falha closed (`-1`) para ocorrência 2+ — o que NÃO bate com "abriu e mostrou concatenado", + mas é a hipótese mais provável de descasamento estrutural. +- **Hipótese mais provável, não confirmada**: `field.length`/`startPosition` retornados pela + API para o campo da 1ª ocorrência dessa linha estão inflados (cobrindo até o fim do bloco de + 600, em vez de só o trecho da própria ocorrência) — nesse caso o slice bruto do TXT + legitimamente inclui o texto real das ocorrências seguintes (que fisicamente estão logo + depois, sem padding entre elas), enquanto a leitura mostra só `field.value` (curto, correto) + com padding invisível por trás. Isso seria um problema de CONTRATO/dado vindo da API para + linhas com grupo repetitivo, não um bug isolado de front. `inspectPositionalField` compara + `currentValue` contra `parsedValue.padEnd(expectedLength)` — se `parsedValue` (o `field.value` + da API) também já vier "vazado" com o texto das ocorrências seguintes (não confirmável sem o + JSON real), a checagem passaria e o buraco ficaria visível só no editor. + +**Não implementei fix especulativo** para não mascarar um possível problema real de contrato +da API em uma feature seguranca-crítica (edita o TXT em produção). Próximo passo: pedir ao +usuário (ou capturar via `logService`/network tab) o JSON de `parseResult.fields` para a +LINHA081 desse documento — especificamente `startPosition`/`length`/`occurrence`/`value` de +cada uma das 4 ocorrências — para confirmar qual das duas pontas (front slicing vs. dado da +API) está realmente errada antes de tocar em `positionalFieldEdit.ts`. + +Ver também [[project_xml_transformation_toggle]] para o padrão geral de "leitura usa +`field.value`, edição faz fail-closed slicing bruto" já documentado para o resto do fluxo. diff --git a/.claude/agent-memory/lp-front-dev/project_transformation_candidate_id_gap.md b/.claude/agent-memory/lp-front-dev/project_transformation_candidate_id_gap.md new file mode 100644 index 0000000..df260d3 --- /dev/null +++ b/.claude/agent-memory/lp-front-dev/project_transformation_candidate_id_gap.md @@ -0,0 +1,24 @@ +--- +name: transformation-candidate-id-gap +description: TransformationCandidate não expõe nome amigável do mapper nem layoutOutputTarget — só candidateId; abas de candidatos do mesmo pathway usam recorte do candidateId como diferenciador +metadata: + type: project +--- + +`src/types/transformation.ts` (`TransformationCandidate`, linhas 56-64) só tem `candidateId`, +`pathway`, `transformedXml`, `score`, `segmentMappings`, `validation`, `failureReason`. Não há +campo de nome do Mapeador Sysmiddle nem `layoutOutputTarget`. + +Quando há múltiplos candidatos `sysmiddle` para o mesmo documento +(`XmlTransformationDisplay.tsx`, seletor `.xml-transformation-candidate-btn`), os botões +mostravam só "Sysmiddle" para todos, sem diferenciação (2026-08-23). + +**Fix aplicado**: `buildCandidateDifferentiator()` em `XmlTransformationDisplay.tsx` extrai um +recorte curto do próprio `candidateId` (que por convenção do back-end é +`"sysmiddle-{MapperGuid}"`) e mostra como sufixo do label, ex. "Sysmiddle — a1b2c3d4…". Não é +um nome legível — é só o suficiente pra distinguir abas. + +**Bloqueio real**: nome amigável do mapper e `layoutOutputTarget` **não existem no contrato +hoje**. Para resolver de verdade, `execute-candidates` (API) precisaria devolver esses campos +em `TransformationCandidate`. Isso é pedido para a equipe da API / `@lp-contract-qa`, não algo +que dá pra inventar no front. diff --git a/.claude/agent-memory/lp-product-manager/product-governance.md b/.claude/agent-memory/lp-product-manager/product-governance.md index 90b62d4..efa68a5 100644 --- a/.claude/agent-memory/lp-product-manager/product-governance.md +++ b/.claude/agent-memory/lp-product-manager/product-governance.md @@ -17,3 +17,17 @@ Primeiro incremento ativo: edição segura de campo no TXT posicional. A ediçã fail-closed: só ocorre quando linha, `startPosition` 1-based e `length` permitem resolver um intervalo exato; o novo valor deve ocupar exatamente o mesmo número de caracteres. A transformação subsequente usa o TXT editado, sem deslocar qualquer campo. + +IDs de campo do Project 3 (para `gh project item-edit`), reusar em vez de re-descobrir via +`gh project field-list`: `PROJECT_ID=PVT_kwDODnBfYs4BgM9h`. Status +`PVTSSF_lADODnBfYs4BgM9hzhaaW7I` (Backlog `8247617d`, Ready `5c74a199`, In Progress `b692f099`, +In Review `1047fa44`, In Validation `e84d3fd4`, Done `7f774e3a`, Blocked `33b609c0`). Tipo +`PVTSSF_lADODnBfYs4BgM9hzhaaXLc` (Epic `59b1b69f`, PBI `75fd3645`, Story `bb1f3c47`, Task +`c9aa38e2`, Bug `f912dca7`, Gate `83341334`). Dono `PVTSSF_lADODnBfYs4BgM9hzhaaXLg` (um option id +por agente, ex. `lp-qa` `b2363da4`, `lp-front-dev` `651e9b69`). Prioridade +`PVTSSF_lADODnBfYs4BgM9hzhaaXMA` (P0 `717f401f`, P1 `3b4b7ea8`, P2 `878b14b4`, P3 `85e85ed3`). + +Issue #140 (contrato de polling do fallback de IA, criada por `@lp-contract-qa`/API team) tinha +chegado ao repo sem labels e sem entrar no Project — issues externas/técnicas nem sempre nascem +classificadas; ao encontrá-las, adicionar ao Project 3 e classificar (labels + campos) antes de +tratar como item de backlog rastreável. From eff8454b6563d6c04b4820a403728817fddb4b2f Mon Sep 17 00:00:00 2001 From: Elson Lopes Date: Sun, 23 Aug 2026 19:08:30 -0300 Subject: [PATCH 5/5] fix(docs): corrigir formatacao Prettier na memoria de agentes --- .../project_bff_persistent_logs_and_cloudflare_tunnel_task.md | 2 +- ...roject_cloudflare_tunnel_task_never_registered_2026_08_22.md | 2 +- ...rtualbox_autostart_task_and_login_flakiness_investigation.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.claude/agent-memory/lp-devops/project_bff_persistent_logs_and_cloudflare_tunnel_task.md b/.claude/agent-memory/lp-devops/project_bff_persistent_logs_and_cloudflare_tunnel_task.md index 3666e89..19c1437 100644 --- a/.claude/agent-memory/lp-devops/project_bff_persistent_logs_and_cloudflare_tunnel_task.md +++ b/.claude/agent-memory/lp-devops/project_bff_persistent_logs_and_cloudflare_tunnel_task.md @@ -10,7 +10,7 @@ Scheduled Task `Cloudflared-QuickTunnel` **nunca foi registrada de fato**. `Get- só retorna `LayoutParserFrontend-BFF`; `Get-Process -Name cloudflared` está vazio; `cloudflared-tunnel.log` não existe em lugar nenhum de `C:\`. O binário `C:\Program Files (x86)\cloudflared\cloudflared.exe` está instalado. Ou seja: o texto abaixo -descreve corretamente o *script* (`scripts/Register-CloudflareTunnel.ps1`, revisado de novo em +descreve corretamente o _script_ (`scripts/Register-CloudflareTunnel.ps1`, revisado de novo em 2026-08-19, ver [[project_virtualbox_autostart_task_and_login_flakiness_investigation]]), mas a frase "registers ... as its own Scheduled Task" describe a intenção do script, não um fato já aplicado em produção. A URL pública que ficou cadastrada como redirect URI no Entra diff --git a/.claude/agent-memory/lp-devops/project_cloudflare_tunnel_task_never_registered_2026_08_22.md b/.claude/agent-memory/lp-devops/project_cloudflare_tunnel_task_never_registered_2026_08_22.md index 728a349..3d59c77 100644 --- a/.claude/agent-memory/lp-devops/project_cloudflare_tunnel_task_never_registered_2026_08_22.md +++ b/.claude/agent-memory/lp-devops/project_cloudflare_tunnel_task_never_registered_2026_08_22.md @@ -24,7 +24,7 @@ túnel público caiu). **Por que:** memórias anteriores ([[project_bff_persistent_logs_and_cloudflare_tunnel_task]], [[project_virtualbox_autostart_task_and_login_flakiness_investigation]]) descreviam a -implementação do *script* em termos que soavam como "já registrado em produção" — eram +implementação do _script_ em termos que soavam como "já registrado em produção" — eram descrições da lógica do script, não confirmação de execução real no host. Já foram corrigidas com uma nota apontando para este arquivo. diff --git a/.claude/agent-memory/lp-devops/project_virtualbox_autostart_task_and_login_flakiness_investigation.md b/.claude/agent-memory/lp-devops/project_virtualbox_autostart_task_and_login_flakiness_investigation.md index 85970be..6246eac 100644 --- a/.claude/agent-memory/lp-devops/project_virtualbox_autostart_task_and_login_flakiness_investigation.md +++ b/.claude/agent-memory/lp-devops/project_virtualbox_autostart_task_and_login_flakiness_investigation.md @@ -9,7 +9,7 @@ Requested 2026-08-19 on branch `feat/ia-fallback-polling` (not a dedicated infra did not ask to switch, so the new script was added in place; confirm before it's committed). **Cloudflare Quick Tunnel restart robustness (ask #1):** reviewed -`scripts/Register-CloudflareTunnel.ps1` — the *script* is already correct for the "host reboots, +`scripts/Register-CloudflareTunnel.ps1` — the _script_ is already correct for the "host reboots, nothing works until someone opens PowerShell" problem: Scheduled Task `Cloudflared-QuickTunnel`, `SYSTEM` principal, `-AtStartup` trigger, `RestartCount 999`/`RestartInterval 1min`, log rotation to `logs/cloudflared-tunnel.log`. No domain purchased yet (confirmed again by user), so Quick