chore: establish project baseline

This commit is contained in:
OpenCode
2026-09-21 15:51:47 -03:00
commit 9dcc263217
459 changed files with 447890 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
# Agent company context rules V1
Goal: identify ACTIVE_COMPANY, COMPANY_UF and COMPANY_CRT for each Agent installation deterministically and auditably. Read-only; no fiscal rule is created.
## What the evidence proved (4 SGV databases, `docs/COMPANY_CONTEXT_RESOLUTION_V1.json`)
- **Company identity:** `EMISSOR.EMITENTE_CNPJ` equals exactly one `C000004.CNPJ` in every database, and 100% of the emitted NFC-e access keys (model 65, cUF 29) carry that CNPJ. In RINCAO the other two C000004 rows (SP agro company, refrigeration company) have no emitted documents.
- **Emission authority for CRT/UF/CNPJ = `EMISSOR`:** the emission programs (`GtiVendas_NFCe.exe`, `Envio NF-e.exe`) bind `QrEmissor.EMITENTE_CRT/EMITENTE_UF/EMITENTE_CNPJ` and never reference `C000004.CRT` or `OPTANTE_SIMPLES`. The ERP (`SGV`) binds `C000004.CRT` (`qrFilialCRT`, `QrSPEDFilialCRT`) and `OPTANTE_SIMPLES` for the filial registry/SPED. The CRT tag inside issued XML could not be read: `C000061.NFE_XML` and `XML_MES.XML` store file paths, not content.
- **UF:** EMISSOR.EMITENTE_UF, C000004.UF and the cUF of emitted keys agree everywhere (`UF_SOURCE_CONFIRMED`).
- **CRT:** agrees in CONSTRUCAO only. MMMODAS (EMISSOR 1 / C000004 2), RINCAO (3 / 1), UNIAO (1 / 3) are `CRT_SOURCE_CONFLICT`. OPTANTE_SIMPLES also disagrees with CRT in MMMODAS and UNIAO.
## Resolution algorithm (`agent/src/gti_fiscal_agent/company_context.py`)
1. **Company.** Configured `COMPANY_CODE`/`COMPANY_CNPJ` must identify exactly one C000004 row. Otherwise: one C000004 row → it; several rows → the single row whose CNPJ equals the single EMISSOR CNPJ; anything else → `CONFIG_REQUIRED`. First row / lowest code / latest row are never used.
2. **Emitter cross-check.** EMISSOR must describe the selected company, else `CONFLICT` (also covers a configured company that is not the emitter).
3. **Documents.** If NFC-e keys exist, some must carry the selected CNPJ (none → `CONFLICT`); other CNPJs are only a warning (`HISTORICAL_NFCE_KEYS_FROM_OTHER_CNPJ`, e.g. the test database).
4. **UF.** EMISSOR, C000004 and document-key sources must agree; disagreement → `CONFLICT`, none → `UNRESOLVED`.
5. **CRT.** EMISSOR and C000004 agree → `CRT_SOURCE_CONFIRMED`. They disagree → `CONFLICT` unless `COMPANY_CRT_SOURCE=EMISSOR|C000004` is configured (resolved, `CONFIGURED`, confidence MEDIUM, warning recorded). Single source → accepted with warning. None/invalid → `UNRESOLVED`. Recommendation from the evidence: `EMISSOR`.
6. `OPTANTE_SIMPLES` incoherent with the CRT is reported as a warning, never used to decide.
## Preflight
`python agent/scripts/preflight_company_context.py [--database <FDB>]` prints `COMPANY_CONTEXT=PASS|FAIL`, company_code, masked CNPJ (first/last 2 digits), UF, CRT, source_of_truth, confidence, warnings, reasons; exit code 2 on FAIL. `run_agent_service.py` runs it at startup and aborts on FAIL. `read_company_context()` raises `CompanyContextError` on FAIL. It fails on: several companies without selection, configured CNPJ/code not found, CRT or UF undeterminable, emitter/selection divergence, unresolved CRT conflict.
## Results without configuration
| database | class | reason |
|---|---|---|
| CONSTRUCAO | AUTO_RESOLVED | single row, all sources agree |
| MMMODAS | CONFLICT | CRT EMISSOR=1 vs C000004=2 |
| RINCAO | CONFLICT | company resolved through EMISSOR CNPJ; CRT EMISSOR=3 vs C000004(row)=1 |
| UNIAO | CONFLICT | CRT EMISSOR=1 vs C000004=3 |
With `COMPANY_CRT_SOURCE=EMISSOR` the three resolve (`CONFIGURED`); a configured non-emitting company or an unknown CNPJ still fails (tested on RINCAO).
## Minimal generic Agent requirement
C000004 + C000025; `supports_ncm`, `supports_ipi`, `supports_cest`; a resolvable company context (this preflight PASS); the executor's live column type/length guard. Everything else is capability-based (`scripts/sgv_capabilities.py`); schema fingerprint equality is NOT required.