feat: CFOP/IPPT write-capability core (inert, not wired, not activated)
Missao 3 / Bloco C.1-C.2: new agent/src/gti_fiscal_agent/cfop_ippt_auto_apply.py
mirrors the existing rtc_auto_apply.py safety model for a new, narrower
capability (CFOP_IPPT_WRITE_V1): compare-before-write, stale-write guard
("human override sempre vence" - a value changed since the candidate was
selected is never overwritten), one transaction per cell, readback
verification, and rollback that only restores a value still equal to what
was written. Candidates come exclusively from field_contract.resolve_product's
own AUTO_FILL_SAFE classification for CFOP/IPPT - no new fiscal rule.
14 new tests (agent/tests/test_cfop_ippt_auto_apply.py) prove: flag OFF means
the caller never reaches a write; only CHANGE+write_eligible CFOP/IPPT cells
become candidates; a human edit between decision and write blocks the write;
a second run over an already-applied cell writes zero; a technical failure on
one cell never stops the batch; rollback refuses when the value changed again.
Deliberately NOT wired into agent/scripts/run_agent_service.py::execute_job,
and the Hub-side authorization/audit plumbing (Installation.cfop_ippt_write_mode,
audit table, job-delivered authorization - the same pattern as
central/app/rtc_audit.py) does not exist yet. No currently-published Agent
behavior (1.3.8) changes with this commit - nothing imports this new module.
See docs/CFOP_IPPT_WRITE_CAPABILITY_V1.md for the acceptance roteiro (test
products only, never real ones) and what remains before this can ever be
activated for a real installation - deliberately left for review before any
further step, per this mission's explicit instruction not to publish it active.
Also: dossie_contador.md (internal + anonymized) updated with the Bloco B
finding that the ~9.5% CST=01 anomaly is confined to a single calendar month
(not the whole year), with no fiscal-dimension (NCM/CFOP/CSOSN/ICMS group)
explaining which items got it - consistent with a one-off technical event,
not a deliberate rule.
REAL_PRODUCT_FISCAL_WRITES=0 (no write path exists in this commit).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
# 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).
|
||||
+14
-8
@@ -18,14 +18,20 @@ PIS/COFINS" → escolher `NOT_APPLICABLE`, `OPERATION_TIME` ou um perfil JSON de
|
||||
## (b) O CST 01 emitido em ~9,5% dos itens é intencional?
|
||||
|
||||
**Evidência**: dos 34.946 itens de NFC-e emitidos pelo Agent 2, 3.336 (9,5%) trazem CST de
|
||||
PIS/COFINS = "01" — mas **3.336 desses 3.669 itens estão concentrados no ano de 2022** (90,9% dos
|
||||
itens daquele ano); nos demais anos com dados (2018, 2023, 2024, 2025, 2026), a taxa é 0%. O ano de
|
||||
2022 já havia sido sinalizado em outra auditoria (`docs/AI_PROJECT_STATE.md`, mission
|
||||
"Product Fiscal Defaults Engine V1") como um período com problema de integridade de dados (drift de
|
||||
identidade de produto em dezembro/2022). A leitura mais provável é que **não é uma política atual
|
||||
deliberada** — é um comportamento isolado de um período específico, possivelmente uma versão antiga
|
||||
do software de emissão ou uma configuração temporária. Não decidimos isso por evidência
|
||||
insuficiente para confirmar a causa exata; a pergunta é se o contador reconhece esse padrão de 2022.
|
||||
PIS/COFINS = "01" — e são **exclusivamente de dezembro/2022** (não "2022" em geral: nenhum item
|
||||
antes de 01/12/2022 ou depois de 31/12/2022 tem o campo preenchido; 90,9% dos itens daquele mês
|
||||
específico têm CST preenchido, os outros 9,1% não). Testamos se NCM, grupo do produto, CFOP, CSOSN
|
||||
ou o grupo de ICMS do item explicam quais itens recebem o CST: todos os 3.336 itens preenchidos
|
||||
compartilham o mesmo grupo de ICMS (vazio), e apenas 10 NCMs e 2 CFOPs aparecem no conjunto todo —
|
||||
ou seja, não há uma fórmula fiscal por trás disso, é um conjunto pequeno e homogêneo de vendas reais
|
||||
(refrigerante, macarrão, cerveja, biscoito — produtos plausíveis, não lixo de teste) concentrado
|
||||
num único mês. Esse mesmo mês (dezembro/2022) já havia sido sinalizado em outra auditoria interna
|
||||
como um período com problema de integridade de dados (identidade de produto instável nas notas
|
||||
daquele mês). A leitura mais provável é que **não é uma política fiscal deliberada** — é um efeito
|
||||
técnico de um evento pontual (ex.: uma migração de dados ou uma versão diferente do software de
|
||||
emissão usada só naquele mês), não uma regra de negócio. Não decidimos isso por evidência
|
||||
insuficiente para confirmar a causa exata; a pergunta é se o contador reconhece esse episódio de
|
||||
dezembro/2022.
|
||||
|
||||
## (c) CFOP_ENTRADA no cadastro do produto é necessário ou é sempre definido na nota de compra?
|
||||
|
||||
|
||||
Reference in New Issue
Block a user