Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .claude/agent-memory/lp-devops/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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.

Expand Down
Original file line number Diff line number Diff line change
@@ -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).
Original file line number Diff line number Diff line change
@@ -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).
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions .claude/agent-memory/lp-front-dev/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Original file line number Diff line number Diff line change
@@ -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 `<input>` 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 `<input>` 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.
Original file line number Diff line number Diff line change
@@ -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.
Loading
Loading