Appearance
Regulatory analyze
The workbench is a CEMAC due-diligence engine, not a single-country tool. Congo (CG) is the default jurisdiction; SEEG (Gabon) is a showcase dataset. All checks are deterministic (no LLM).
Two layers must not be confused:
| Layer | Owner | What it is |
|---|---|---|
Catalog (regulatory_engine/rules/catalog.ts) | code (versioned Regulation corpus) | Rules evaluated by analyzeDeal; always available, offline-safe |
Vault (LegalInstruments / Editions / Provisions / SourceArtifacts) | platform (locus-ops) | Reviewed statute PDFs + provision text; overlaid onto evidence, never a replacement for catalog evaluation yet |
text
analyzeDeal(project) ← catalog + deal evidence → findings/checks/coverage
│
▼ (server only, with session)
analyzeDealWithVault(project, ctx)
1. report = analyzeDeal(project)
2. enrichRegulatoryEvidenceWithVault → GET /api/legal-vault/resolve per evidence item
3. replaceRegulatoryFindingsInSor → PUT /api/deals/{dealId}/regulatory-findings
→ DossierRegulatoryFindings (Vault* ids)Jurisdiction layer (technical documentation §6)
| Module | Role |
|---|---|
src/jurisdiction/ | countries.ts (CEMAC registry, default CG), framework-map.ts (authorities, transaction types), resolver.ts (resolveDealProfile) |
src/regulatory_engine/ | rules/catalog.ts (versioned Regulation corpus), versioning/ (effective-date logic), applicability/ (which rules apply), citations/, analyze.ts (typed Findings, checks, coverage), memo.ts (regulatory-basis memo section) |
src/types/{jurisdiction,regulation,finding}.ts | CEMACCountry, DealJurisdictionProfile, Regulation, Finding / FindingType |
Resolution flow: deal profile (primary + involved jurisdictions, transaction type, public offering / cross-border / government-related) → regional layer (OHADA, SYSCOHADA, CEMAC, COSUMAF, BEAC, COBAC) + national layer of every involved country → filtered by the transaction date (effectiveFrom / effectiveTo) and applicability conditions (temporal applicability engine: excludes repealed articles and cites gazette volume, article and SHA-256 checksum).
Findings are classified and always carry jurisdiction, framework, source, evidence and confidence:
FindingType |
|---|
document_contradiction, financial_inconsistency, contractual_breach, regulatory_issue, regulatory_gap, missing_document, legal_risk, calculation_issue |
Mapping from contradiction alerts: topic INTEREST_RATE|MATURITY_DATE|COVENANT → document_contradiction, EBITDA_DEFINITION → financial_inconsistency, NEGATIVE_PLEDGE → contractual_breach; severity CRITICAL → HIGH, MAJOR → MEDIUM, MINOR → LOW.
Known limits: regional entries are catalogued by institutional reference only (no article text loaded); national corpora are pending placeholders reported as regulatory_gap. All references must be validated by counsel before use in a credit committee.
Client path
analyzeDeal(project) in regulatory_engine/analyze.ts (domain twin in domains/regulatory/analyze.ts kept in sync) — findings, checks, curation stats from catalog + deal evidence.
RegulatoryReport:
| Field | Meaning |
|---|---|
regulatory | resolved RegulatoryProfile |
findings | Finding[] with regulatoryEvidence[] and dealEvidence[] |
checks | DealCheck[] |
coverage | round(catalogued / applicable × 100) (0 when no rule applies) |
frameworkCuration | per-authority FrameworkCurationStat (human_verified / machine_validated / source_verified / pending) |
incompleteNationalWarning | national corpus pending |
counts | { issues, questions, verified, highSeverity } |
Server path
analyzeDealWithVault(project, sessionCtx):
- Run
analyzeDeal - Overlay vault IDs via
enrichRegulatoryEvidenceWithVault - Optionally
replaceRegulatoryFindingsInSor→DossierRegulatoryFindings
mermaid
sequenceDiagram
autonumber
participant S as Server (BFF / locus-server)
participant E as analyzeDeal
participant V as /api/legal-vault/resolve
participant R as PUT /api/deals/{dealId}/regulatory-findings
S->>E: project
E-->>S: report{findings, checks, coverage}
loop each regulatoryEvidence item
S->>V: instrumentId=item.regulationId, provisionNumber=item.article
V-->>S: editionId, provisionId, artifactId, sha256, provisionText, pageStart
end
S->>S: item += vaultEditionId, vaultProvisionId, vaultArtifactId, vaultSha256, page, statutoryExcerpt, openStatuteHref
S->>R: replace-all {findings[]} (findingKey = f.id)Overlay rules (vault_enrich.ts):
| Rule | Detail |
|---|---|
| Fail-soft | resolveVaultProvision returns null when the SoR is unreachable or !res.ok → catalog text kept |
| Fields added | vaultEditionId, vaultProvisionId, vaultArtifactId, vaultSha256, page (from pageStart), statutoryExcerpt (from provisionText) |
| Open statute | openStatuteHref = /api/locus/legal-vault/artifacts/{artifactId}/content when an artifact exists |
| Evaluation source | Catalog text remains the evaluation source until vault text is legal_reviewed |
| Persistence | PUT is replace-all and idempotent per run; findingKey = finding id; primary evidence supplies regulationId, articleNumber, statutoryExcerpt, Vault* ids; full evidence in EvidenceJson |
Persisted columns: Legal vault & Watchtower → DossierRegulatoryFindings. Read back with GET /api/deals/{dealId}/regulatory-findings (regulatory:read), BFF /api/locus/deals/[id]/regulatory-findings.
Heuristics (slice 34)
- BEAC Art. 42: non-XAF currency or data-room wording (EUR/USD, emprunt extérieur, devise).
- COSUMAF APE: deal type + title tags + investor/listing keywords vs private-placement language.
Implementation notes (analyze.ts):
| Heuristic | Signals |
|---|---|
BEAC Règlementation des Changes Art. 42 (beac-reg-changes-2018, "Article 42") | FOREIGN_LOAN_SIGNAL: EUR/USD/GBP/CHF, euros, dollars US, emprunt extérieur / en devises, prêt/financement/dette extérieure, devise(s), foreign-currency, external loan/borrowing/debt in deal-room page titles/snippets/summaries — triggers finding F-fx (regulatory_issue, severity LOW, status question) when the profile currency ≠ XAF or a document mentions foreign/external borrowing, even if the declared profile currency is XAF |
| COSUMAF APE vs private placement | PRIVATE_PLACEMENT language (investisseurs qualifiés/institutionnels, placement privé, information memorandum) and title tags vs PUBLIC_OFFER_SIGNAL (appel public, offre au public, prospectus, note d'information, visa COSUMAF, admission à la cote, BVMAC, listing, retail); conflict = private language alongside a declared APE, or — for bond_issuance — alongside public/listing vocabulary even when no APE is declared |
Evidence helper findDocEvidence is fail-soft: malformed documents yield null, never an exception; citations are cite_reg_{docId}_{page} and reuse the page bbox from the deal room (no invented coordinates).
Applicability
LegalProvisionApplicability CRUD in ops (GET/POST /api/admin/regulatory/applicability, PUT/DELETE …/{id}); bootstrap seeds imported rows. Not yet consumed inside analyzeDeal (deferred).
Bootstrap seeds (LegalVaultBootstrap.EnsureApplicabilityAsync, id app_{provisionId}):
| Provision id | Transactions | Flags | Note |
|---|---|---|---|
cosumaf-rg-2014-art-4 | bond_issuance, equity_issuance | publicOffering | APE qualification |
cosumaf-rg-2014-art-28 | bond_issuance, equity_issuance | publicOffering | Visa / note d'information |
cosumaf-inst-ape-2019-art-3 | bond_issuance | privatePlacement | Private-placement carve-out |
beac-reg-changes-2018-art-42 | — | foreignCurrency, externalBorrowing | Prior declaration of external / foreign-currency borrowing |
syscohada-audcif-2017-art-17 | — | — | Balance sheet equilibrium (every SYSCOHADA statement) |
ohada-aus-2010-art-138 / -140 | secured_loan, syndicated_loan, project_finance | accountPledge | Account pledge enforceability / effects |
Rows stay imported until ops verify; the seed is idempotent and never overwrites ops edits.
Related engines and rules
- Prospectus pre-visa fee audit: 3.50 % statutory fee ceiling (
> 3.50 % → VISA_PRE_REJECTED,≤ 3.50 % → VISA_PRE_APPROVED), cryptographically signed pre-visa attestation (REGULATORY_ATTESTATION_HMAC_SECRET). - Regulatory module cannot be disabled in
PUT /api/organizations/current/entitlements. - Copilot
check_regulatory_complianceevaluates catalog + deal payload;regulatory_corpusscope blocks financial calcs. - Workspace routes:
regulatory,cosumaf-auditor(Analysis zone).
Tests: regulatory_engine.test.ts, regulatory_domain.test.ts, regulatory_and_precedents_persistence.test.ts, regulatory_admin_vault.test.ts, locus-workers/tests/test_regulatory_engine.py; ops: vault_publisher_extract_findings.test.ts.