Release 2026-05-26 — Requêteur IA, cession Kankele 3-parties, anonymisation marketplace + analyse IA enrichie
Historique — décrit des surfaces déposées au Lot 1 (2026-09) du programme « salle de marché institutionnelle » (marketplace investisseur,
/marketplace.html, endpoints/v2/marketplace/*). Conservé pour mémoire. Voirdocs/legal/2026-09-14-fermeture-consultation-investisseur.mdetdocs/releases/2026-09-24-salle-de-marche-lots-0-8.md.
Status : en production. tauraco.io, tauraco.ai, api.tauraco.io tous synchronisés.
TL;DR (5 secondes)
| Nouveauté | Surface utilisateur | Disponibilité |
|---|---|---|
| Requêteur IA en langage naturel | /app/assistant | Tenants PREMIUM, tous rôles |
| Cession Kankele 3-parties | Seller portal + ERP factures clients | Tous tenants |
| Anonymisation marketplace GLOBALE | /marketplace.html + toutes endpoints investisseur | Tous investisseurs |
| Analyse IA enrichie par facture (Polymarket-style) | Modal "Voir l'analyse" sur chaque opportunité | Tous |
| Mapping NAF → secteur | Automatique au publish de listing | Tous |
| Dark mode charbon premium | Toggle existant | Tous |
| SPA fallback /app/* | Tous deep-links Vue Router | Tous |
Détail des PRs livrées
PR #38 — style(theme): refonte dark mode — charbon premium neutre
Bascule du fond bleu nuit saturé #0A1A2F vers un charbon neutre #0D1014 (façon Mercury / Brex / JPM Private Bank). Accent turquoise tempéré, success sage, danger terracotta, borders 10% → 6% blanc. Aucune modif fonctionnelle.
PR #39 — feat(cession): Kankele signature 3-parties sur chaque entrée marketplace
Toute soumission au marketplace (seller portal + ERP factures clients) ouvre désormais une enveloppe Kankele à 3 signataires :
- Fournisseur (le cédant qui soumet la facture)
- Payeur (le débiteur cédé — l'utilisateur saisit son email + titre + commentaire)
- Tauraco (mandataire de l'investisseur ; l'investisseur réel signera plus tard via avenant à l'allocation)
La facture reste cachée du marketplace public jusqu'à signature complète des 3 parties. Deux contrats générés : factoring_assignment_agreement + buyer_notification_cession (nouveau template).
Files :
- backend :
services/cession_service.py,domains/contracts/templates/buyer_notification_cession_fr.md - frontend :
SellerInvoicesView.vue(dialogue cession enrichi),ErpSalesInvoiceDetailView.vue(bloc signature dans le dialogue financement)
PR #40 — feat(requeteur): read-only conversational data access (premium)
Nouvelle surface POST /v2/ai/requeteur (page UI /app/assistant) qui permet aux dirigeants non-comptables de PME d'interroger leurs propres données en langage naturel (FR + EN).
Trois catalogues d'outils strictement séparés, le LLM ne génère aucune SQL :
| Access type | Couverture | Outils |
|---|---|---|
| ACCESS_ERP_PLUS_FIN | ERP + financement | 12 (ventes, clients impayés, achats, trésorerie, financements…) |
| ACCESS_FIN_ONLY | Financement seul | 5 (statut factures, coût, décaissements) |
| ACCESS_INVESTOR | Investisseur | 8 (positions, rendement, opportunités anonymisées, exposition) |
Couches de défense indépendantes :
- Catalogue par isolation d'import (les autres catalogues pas en mémoire)
- Schémas Pydantic
extra="forbid"+ liste noire{tenant_id, user_id, …}rejetée au dispatch - Rôle Postgres dédié
tauraco_requeteur: SELECT-only sur allow-list,default_transaction_read_only=on,statement_timeout=5s - Output filter : tout batch est droppé si une ligne sort du scope (audit
REQUETEUR_ISOLATION_BREACH) - Audit log par appel via
AuditLogServiceexistant (visible dans/ops/audit-logs) - SUPER_ADMIN / ADMIN refusés au endpoint (403)
- Paywall :
subscription_tier == PREMIUMrequis (env-overridable)
Setup ops :
REQUETEUR_RO_PASSWORD+DATABASE_RO_URLdans/opt/tauraco/.env- Migrations Alembic 030 + 031 (rôle + GRANTs)
PR #41 — feat(requeteur): NAF division → coarse sector mapping
12 secteurs coarse, mapping NAF Rev. 2 (88 divisions INSEE), labels FR+EN. Le marketplace_service.publish_invoice() lit Tenant.naf_code et écrit naf_code + sector dans la factsheet à la création du listing.
PR #42 — chore(scripts): seed naf_code on tenants from a name heuristic
Utilitaire idempotent backend/scripts/seed_tenant_naf_codes.py avec --dry-run et --refresh-listings. À lancer manuellement après chaque cohorte d'inscriptions seller. Jamais déclenché par l'agent.
PR #43 — docs+sec(requeteur): NAF invariant + k-anonymity & amount bucketing
Renforce l'anonymisation contre la ré-identification par corrélation (défaut détecté lors des tests réalistes du Volet 2) :
- k-anonymity floor :
apply_k_anonymity_on_sector(k_min=3)— un secteur apparaissant moins de 3 fois dans le résultat est collapsé enOTHER. Empêche "il n'y a qu'un seul AGRI → c'est forcément X". - Amount bucketing :
bucket_amount()arrondit par paliers (500 / 1k / 5k / 10k selon ordre de grandeur). "18 750 EUR" devient "19 000 EUR". - Duration bucketing : ceil semaine (≤90j) ou mois (>90j).
Documentation : invariante NAF dans CLAUDE.md, procédure manuelle de seed dans docs/runbook.md.
PR #45 — feat(marketplace): anonymisation globale + analyse IA enrichie par facture
Constat utilisateur sur la prod : https://tauraco.io/marketplace.html montrait toujours supplier_name (le requêteur PR #40 anonymisait sa propre surface uniquement) ; et aucune analyse marché/risque pour aider l'investisseur non-expert à décider.
Volet anonymisation globale :
- Nouveau
services/listing_anonymizer.project_for_investor(), un seul code path, appliqué à/v2/marketplace/public/listings,/listings,/listings/{id}et/v3/portal/investor/investments. - Champs systématiquement strippés :
supplier_name,buyer_name,tenant_id,invoice_number,siren/siret,extracted_data. - Remplacés par
supplier_label= "Entreprise vérifiée — \<Secteur\>",amount_ttcbucketé (réutilisebucket_amountde PR #43),duration_daysbucketé. - Seller portal inchangé (c'est leur propre donnée).
project_for_admin()reste disponible pour ops, jamais montée sur une route publique.
Volet analyse IA Polymarket-style :
services/investor_brief_service.generate_brief(): appel Claude Sonnet 4.6 par listing, 4 paragraphes structurés (opportunity/market_context/risks/why_we_finance) plus un blocexpertrepliable (risk_flags, score_numeric, P(paiement), …).- Sécurité critique : la factsheet est *redacted* avant l'appel — Claude ne reçoit JAMAIS
supplier_name. Même jailbroken, il ne peut pas echo l'identité. - Pydantic schema, hash-idempotent (
{grade, amount, sector, flags}), failure-safe (Claude down → publish OK, brief généré au prochain backfill).
UI :
- Static
marketplace.html: modal "Voir l'analyse complète" avec KPIs + 4 sections +<details>mode expert. 100%createElement+textContent(XSS-safe). - Vue
MarketplaceListingsView.vue: section "Analyse Tauraco" dans le detail panel, mêmes 4 sections + mode expert + disclaimer réglementaire.
Backfill exécuté : 10/10 briefs générés sur les listings les plus récentes (script backend/scripts/backfill_investor_briefs.py --limit 10, ~$0.13).
Audit déploiement
| Surface | Avant | Après |
|---|---|---|
Backend api.tauraco.io | code obsolète + workers depuis 3 semaines | Code main 20c9468 + workers fraîchement restartés, 451 routes, migrations 031 (head) |
Frontend tauraco.io/app/ | bundle obsolète | bundle frais index-DSHZ_20b.js |
| SPA fallback | absent (404 sur /app/login etc.) | .htaccess shipped via Vite public/ |
| tauraco.ai (Next.js) | déjà à jour (8j) | inchangé |
Tests E2E exécutés en prod
| Domaine | Vérif | Résultat |
|---|---|---|
| tauraco.io | 9 deep-links (login, assistant, marketplace, seller, erp, investor, pipeline) | 9/9 → 200 |
| tauraco.ai | 4 pages marketing (home, market, investors, insights) | 4/4 → 200 |
| api.tauraco.io | 4 endpoints publics + 4 endpoints d'auj (cession, requeteur, kankele) | tous OK (200 ou 401 = auth requise) |
| Requeteur | Investor PREMIUM, FR+EN, 6 prompts adversariaux | 0 leak, 0 REQUETEUR_ISOLATION_BREACH |
| Marketplace anonymisation | 50 listings inspectés via curl | 0 fuite supplier_name/buyer_name/tenant_id |
| Briefs IA | Backfill prod 10 listings | 10/10 générés (après cap Pydantic relevé à 1500 chars + retry) |
Setup à compléter par l'opérateur (post-merge)
- NAF seed sur les nouveaux SELLER tenants :
docker compose exec backend python -m scripts.seed_tenant_naf_codes --dry-runpuis sans--dry-runsi OK. - Kankele production : config par défaut en
DEV_MODE=True(mock). Pour brancher la prod, voirdocs/kankele_setup.md. - Premium tier : pour ouvrir le requêteur à un tenant, soit via le flow MoMo upgrade, soit
UPDATE tenants SET subscription_tier='PREMIUM' WHERE id='…'. - Briefs IA — élargir le backfill au-delà des 10 plus récentes si besoin :
docker compose exec backend python -m scripts.backfill_investor_briefs --limit 50(~$0.65). - Revue humaine des 10 premiers briefs en prod — vérifier le ton (factuel, pas de promesse de rendement) et la conformité réglementaire.
Statistiques de la journée
| PR | Insertions | Deletions |
|---|---|---|
| #38 | 49 | 43 |
| #39 | 452 | 127 |
| #40 | 3 202 | 2 |
| #41 | 378 | 34 |
| #42 | 149 | 0 |
| #43 | 514 | 6 |
| #44 | 133 | 0 |
| #45 | 1 157 | 39 |
| Total | 6 034 | 251 |
107 tests unitaires verts au total :
- 93 tests requêteur (
pytest tests/requeteur/) — couvre catalogues, isolation, k-anonymity, NAF, anonymisation - 14 nouveaux tests marketplace (
pytest tests/marketplace/) — anonymizer partagé + générateur de briefs (redaction LLM input, idempotence, failure-safe)