chore: move CFOP/IPPT write-capability code off main to feature/cfop-ippt-write

Missão 4 / Bloco B.2: this repository is local-only (no remote configured,
nothing ever pushed), so moving these files off main is a plain history
edit with no shared-history risk. agent/src/gti_fiscal_agent/cfop_ippt_auto_apply.py,
its tests and docs/CFOP_IPPT_WRITE_CAPABILITY_V1.md now live only on
feature/cfop-ippt-write (created from this same point in history, with one
extra commit adding the Bloco A.3 end-to-end regression test). Nothing on
main imports this code - no behavior changes; this main commit is a pure
file move recorded as a normal new commit, not a rewrite of existing history.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
gti
2026-09-28 10:53:45 -03:00
co-authored by Claude Sonnet 5
parent 1abb7e516e
commit 0973c41580
3 changed files with 0 additions and 465 deletions
-67
View File
@@ -1,67 +0,0 @@
# CFOP/IPPT write capability V1 — Missão 3 / Bloco C
## Estado atual: núcleo implementado e testado, NÃO ligado, NÃO publicado
- `agent/src/gti_fiscal_agent/cfop_ippt_auto_apply.py` — lógica pura (sem I/O de rede): `write_gate`,
`select_candidates` (só células `CHANGE`+`write_eligible=True` de `field_contract.resolve_product`,
restrito a CFOP/IPPT — nenhuma regra nova, reaproveita o motor já testado), `apply_candidate`
(compare-before-write, stale-write guard = "human override vence", readback, uma transação por
célula), `apply_candidates` (lote, uma falha nunca para o resto), `rollback_one`/`rollback_records`.
- `agent/tests/test_cfop_ippt_auto_apply.py` — 14 testes, todos verdes: flag OFF nunca escreve; só
células AUTO_FILL_SAFE viram candidatas; valor alterado por humano entre a decisão e a escrita
aborta sem gravar (`STALE_VALUE_CHANGED_SINCE_DECISION`); segunda execução sobre a mesma célula já
aplicada grava 0 (`NO_WRITE_REQUIRED`); readback divergente nunca retorna `APPLIED`; rollback só
restaura se o valor atual ainda for o que foi escrito.
- **Nada disto está ligado ao `execute_job` do Agent, nem existe ainda o lado Hub** (coluna
`Installation.cfop_ippt_write_mode`, tabela de auditoria, entrega da autorização dentro do job -
o mesmo padrão já usado pelo RTC, `central/app/rtc_audit.py`/`installations.rtc_write_mode`).
**Nenhum comportamento do Agent publicado (1.3.8) muda com este commit** — nenhum código existente
importa este módulo novo.
## Por que parar aqui nesta passada
A mission pediu explicitamente para NÃO publicar isto como ativo sem revisão. Ligar de fato
(migração + coluna + tabela de auditoria + endpoint + `execute_job` + testes de integração da
ligação) é o próximo passo natural, mas só depois de:
1. Você revisar este relatório e o Bloco C.1 (candidatas rebaixadas).
2. Decidir se a exclusão manual das 9 células de evidência fraca (ver Bloco C.1) deve ser um
mecanismo do capability em si (ex.: lista de exclusão por produto) ou uma melhoria futura na
evidência do `field_contract` (contagem/proporção de `C000032.TIPO=COMPOSICAO`, hoje é só um
`in`/`not in` binário no Agent, nunca uma taxa).
## Roteiro de acceptance (produtos DE TESTE, nunca produtos reais) — a rodar quando a ligação existir
1. **Preparar**: operador cria 2 produtos de teste no SGV de teste/sandbox (nunca em produção):
um com CFOP em branco e papel de produto comprovável (ex.: `TIPO=VENDA`, evidência de revenda
clara), outro com IPPT="T" mas uma entrada `C000032.TIPO=COMPOSICAO` clara e majoritária para o
mesmo CODPRODUTO.
2. **Rodar REPROCESS_PRODUCTS em modo DRY_RUN** (capability ligada, `cfop_ippt_write_mode=DRY_RUN`):
confirmar que o job relata os 2 candidatos, sem nenhuma escrita (`writes_attempted=0` no
resultado do job).
3. **Ligar `cfop_ippt_write_mode=ENABLED`** só para essa instalação de teste; rodar de novo:
confirmar `write_successes=2`, os 2 valores corrigidos no Firebird, auditoria com `old_value`/
`new_value`/`job_id` para cada célula.
4. **Editar manualmente** um dos 2 produtos no SGV para um TERCEIRO valor (nem o antigo nem o
proposto) e disparar `REPROCESS_PRODUCTS` de novo sem terceira mudança de cadastro: como o valor
já bate com o proposto ou já foi decidido, a resposta esperada é `write_successes=0`
(`NO_WRITE_REQUIRED` se já correto, ou nada a propor se o motor já vir o valor humano como
`ALREADY_CORRECT`/válido).
5. **Critério de PASS**: (i) flag OFF = 0 escritas em todos os passos; (ii) só CFOP/IPPT tocados,
nunca outro campo; (iii) segunda execução idêntica = 0 escritas; (iv) edição humana entre a
decisão e a escrita nunca é sobrescrita; (v) toda escrita aparece na auditoria com valor antigo,
novo, `job_id` e timestamp; (vi) rollback de uma célula aplicada restaura o valor antigo só se
ninguém mexeu de novo.
Só depois de um PASS completo nesse roteiro (produtos de teste) é que as 12 células reais do Agent 2
(Bloco C.1) devem ser consideradas para uma primeira aplicação real — e mesmo assim, só as que
sustentam a evidência (ver rebaixamento no Bloco C.1), nunca o lote inteiro de uma vez.
## Se/quando o Agent mudar de fato (fora desta passada)
Bump para 1.3.11 (release/VERSION, `agent/pyproject.toml`, `agent/scripts/run_agent_service.py`'s
default, `agent/src/gti_fiscal_agent/preflight.py`, todos juntos - mesma disciplina de
`test_agent_version_is_internally_coherent`), build do installer
(`scripts/build_agent_package.py`), validação em Windows Sandbox (`scratch_sandbox/gti.wsb` já
existe de releases anteriores: instalar, `upgrade in-place` preservando `installation_id`/SQLite,
confirmar `cfop_ippt_write_mode` chega `DISABLED` por padrão mesmo depois do upgrade). O upgrade na
máquina real do Agent 2 continua sendo do operador (sem canal remoto).