feat: visible per-product enrichment status, flagged re-evaluation (panel/Reprocessar/Sincronizar/KB decision), operator NCM decision, deterministic name-only match, stale-write guard; 1.3.5

This commit is contained in:
gti
2026-09-26 14:37:37 -03:00
parent ab65ddd679
commit 1333ac1bc4
34 changed files with 804 additions and 42 deletions
+16
View File
@@ -117,3 +117,19 @@ The SGV refuses to save a product without NCM, so operators type a filler. An em
Both tables are identical (parity test). The audit records `old = 00000000`.
- Scope reminders: products created while the function was DISABLED / WAITING stay history (re-baseline); REPROCESS_ALL/SYNC do not push products through the enrichment (only NEW products after the baseline do);
a product WITHOUT EAN is resolved by name only from a unique VERIFIED_KB match.
## Product-visible pipeline, re-evaluation and operator decision (1.3.5, migration 0031)
- **No silent product**: every tracked product has `ui_status` + `reason_code` (Agent detail, section *Funcionalidades*): ENRIQUECIDO, PRONTO_PARA_PREENCHER, SIMULADO, AGUARDANDO_EVIDENCIA, EAN_AUSENTE, EAN_INVALIDO, NOME_AMBIGUO, SEM_MATCH_DE_PRODUTO,
NCM_NAO_VERIFICADO, CONFLITO_DE_NCM, PROVIDER_SEM_QUOTA, AGUARDANDO_RETRY, HUMAN_OVERRIDE, TECHNICAL_PLACEHOLDER (note), WRITE_FAILED, VERIFICACAO_POS_WRITE_FALHOU, NADA_A_PREENCHER, with EAN, detection time, KB status, candidates, last attempt and next retry.
- **Re-evaluation** (`product_enrichments.reevaluate`): set by the panel (*Reavaliar* / *Reavaliar todos os pendentes*), by **Reprocessar / Sincronizar** (`create_job`, even when an equivalent job is already queued) and by a KB decision; the Agent polls
`GET /v1/products/enrichment/pending` every 10 s and re-runs the normal resolve for those tracked, not-yet-enriched products (KB/cache first; the Hub decides about providers; enriched / human-overridden products are never flagged).
Price edits are NOT a new product; *Reavaliar* is the explicit new evaluation. REPROCESS_ALL does not push the whole catalogue through the enrichment: only the tracked unresolved set.
- **Stale-write guard** (Agent): a write is refused (`STALE_CONFLICT`) when the field no longer holds the value the Hub decided on (a person typed something meanwhile).
- **Name-only** (`kb.alias_match`): the query token set equals an alias token set, or one contains the other and the difference is only neutral category nouns (REFRIGERANTE, BEBIDA...). ZERO / DIET / a flavour / another size are never neutral, so
"COCA COLA LATA 350 ML" = "REFRIGERANTE COCA COLA LATA 350 ML" but != "... ZERO ...". Writing by name still requires ONE matching KB product whose NCM is VERIFIED_KB.
- **Operator decision (ADMIN)**: `POST /kb/attest` (dashboard, *Base de conhecimento*: search by EAN or name, official NCM text next to each candidate, button *Confirmar este NCM*). Only an existing, vigente candidate can be chosen; the others become `STALE (REJECTED_BY_OPERATOR)`;
the family `OPERATOR_ATTESTATION` + the local evidence of that value = VERIFIED_KB with a promotion audit naming the operator; every affected tracked product is flagged for re-evaluation, so the write happens automatically right after the click.
- **RTC != Enrichment**: distinct labels on the Agent detail and a test that each switch never changes the other.
- **Supplier NF-e**: investigated, no independent supplier evidence exists in the five databases (entry-item tables C000022/C000075 are empty; C000061/C000062/NFCE are documents ISSUED by the client from its own master); no `SUPPLIER_NFE` family was created.
- **Free NCM sources re-checked (2026-09-26)**: Kodebar returns NCM only for medicines (CMED) and needs a key; DotCompany API is prepaid; Pesquisa GTIN has a free plan of 5 queries/MONTH (not integrated); OSCBR V3 NCM is unproven. The official GTIN->NCM source is the SEFAZ CCG web service, which needs the client's ICP-Brasil certificate (not used).
+34
View File
@@ -0,0 +1,34 @@
{
"dev_rehearsal": true,
"info": {
"kb_before": "ROWS [('22021000', 'OBSERVED'), ('22030000', 'OBSERVED')]",
"status_before_decision": "EAN_AUSENTE|NAME_ONLY_NO_VERIFIED_MATCH",
"attest": "ATTEST VERIFIED_KB ['CLIENT_DATA', 'OPERATOR_ATTESTATION'] flagged 15",
"status_dry_run_after_decision": "SIMULADO|DRY_RUN_NO_WRITE",
"AC001_after": [
"22021000",
null,
null,
"200004",
"COCA-COLA ORIGINAL LATA 350 ML"
],
"audit": "CLASSIFICACAO_FISCAL:00000000>22021000:APPLIED:VERIFIED_KB",
"ean_latency_s": 2.39
},
"checks": {
"SILENT_FAILURE_FIXED_STATUS_VISIBLE": true,
"NCM_UNCHANGED_UNTIL_DECISION": true,
"ATTESTED_TO_VERIFIED_KB": true,
"DRY_RUN_SHOWS_CANDIDATE_ZERO_WRITES": true,
"NAME_ONLY_NCM_WRITTEN_BY_AGENT": true,
"CCLASSTRIB_PRESERVED": true,
"AUDIT_OLD_PLACEHOLDER_NEW_NCM": true,
"FINAL_STATUS_ENRIQUECIDO": true,
"SECOND_RUN_WRITES_0": true,
"ZERO_PROVIDER_REQUESTS_ON_KB_RESOLUTION": true,
"EAN_PATH_KB_HIT_WRITE": true,
"EXISTING_REAL_NCM_PRESERVED": true,
"HUMAN_OVERRIDE_PRESERVED": true
},
"ALL_PASS": true
}