# 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 ]` 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.