Python 51.6%
TypeScript 46.7%
CSS 1.7%
1# QC26 — repères pour le développement23Spécification produit complète : voir la mission QC26 (design « Editorial Data Intelligence », neutralité, traçabilité, 127 circonscriptions / majorité 64 dérivée des données). Ce fichier résume ce qu'il faut savoir pour intervenir sans casser.45## Règles non négociables6- Aucune donnée sans provenance (source, document, page, date de récupération, méthode d'extraction, confiance). Aucune projection sans incertitude. Aucune position de parti sans document officiel. Aucun résultat live inventé : l'absence de données n'est jamais un zéro.7- Les couleurs et logos des partis viennent uniquement de `engine/qc26/parties.py` et `web/src/lib/parties.ts` ; jamais de hex dans un composant. Logos officiels dans `web/public/parties/<id>/` avec `source.json` ; ne pas les recréer ni recolorer (le PCQ, logo blanc, se pose sur sa pastille marine).8- Le nombre de sièges (127) et le seuil (64) sont calculés depuis la liste DGEQ (`settings` `seats_total` / `majority_threshold`), jamais codés.9- Le LLM (Anthropic, `qc26/llm.py`) propose, la validation décide (`connectors/polls/base.py::validate`, `connectors/parties/documents.py` : citation vérifiée dans le texte). Faible confiance → file de révision (`/api/admin/review`), jamais publié.10- Toute correction humaine passe par `/api/admin/*` avec une raison → `audit_logs`.1112## Où sont les choses13- Moteur : `engine/` (FastAPI :8201). Base SQLite `engine/data/qc26.db` (WAL). Archives `engine/data/archive/{polls,documents,live}` par SHA-256. Copies officielles DGEQ `engine/data/official/`.14- Web : `web/` (Next 16, :8200), rewrite `/api/*` → moteur. Composants du design system dans `web/src/components/`.15- Modèle : `engine/qc26/model/` (aggregate = poids/effets maison/Kalman ; ridings = swing proportionnel + erreurs ; ensemble = v2 : prior fondamentaux, momentum borné, tempérage par les marchés via repondération IPF des simulations, ajustement partielles ; run = orchestration, stats pondérées, `summary.components`, diff). Changer un paramètre = bump `model_version` + entrée `CHANGELOG` dans `api/public.py`.16- Signaux (v2) : `engine/qc26/signals/` — markets (Polymarket Gamma/CLOB), attention (Wikimedia pageviews), media (Serper news + classement LLM ; GDELT best-effort, 1 req/6,5 s), fundamentals (questions de leadership des sondages → satisfaction, meilleur PM, favorabilité…), byelections (DGEQ via Firecrawl, code URL = code DGEQ carte 2017). Tables `signals`, `markets`, `market_prices`, `media_items`, `by_elections`. API `/api/signals*`, `/api/forecast/components`, admin `POST /api/admin/signals/refresh?what=all`. Page `/signaux`.17- Design : tokens clair/sombre dans `web/src/app/globals.css` (`data-theme`, script inline `THEME_INIT_SCRIPT`), Fraunces (display) + Inter + JetBrains Mono ; couleurs de partis via `partyVar()/partyAlpha()` (variables CSS `--p-*`, éclaircies en sombre) — `partyColor()` (hex) réservé aux images OG.18- Soirée électorale : `engine/qc26/live/` (feed officiel, simulation 2022, nowcast). `POST /api/admin/live/simulation/start` rejoue 2022 en accéléré pour tester la chaîne complète.1920## Gotchas21- SQLite : ne jamais tenir une transaction ouverte pendant un appel LLM (commits explicites dans les connecteurs) ; timeout 90 s. Les `DateTime` passent par `TZDateTime` (UTC explicite à la relecture) : toujours `.isoformat()` sur des objets tz-aware.22- Chemins d'archive stockés absolus : lire via `util.resolve_archive_path()` (la base a été créée sur le laptop puis déplacée).23- Sortant·e = drapeau `depute_sortant` de la liste DGEQ des candidatures, puis gagnant de partielle, puis gagnant 2022 transposé (jamais l'inverse).24- Fraunces via `next/font/google` : `axes` seulement sans `weight` (police variable).25- DGEQ : « PCQ » = Parti communiste, le Parti conservateur est « PCOQ » ; CSV en cp1252 avec tiret 0x96 (`util.fix_dgeq_dash`).26- Wikipédia n'est qu'un index d'amorçage (tier 5) : les lignes `secondary-index` sont remplacées par le PDF de la firme dès qu'il est archivé ; les URL médias (La Presse, L'actualité, X…) ne sont pas des documents primaires.27- La règle « pas de candidature → part résiduelle » ne s'active que pour un parti dont la liste est ≥ 90 % complète.28- Next : `useSearchParams` interdit en prerender (passer `searchParams` du serveur) ; images OG satori = `display:flex` sur tout div à plusieurs enfants.29- Déploiement : `mld stage ~/Desktop/Projets/apps-web/qc26 qc26 && mld deploy qc26` (manifeste `deploy/qc26.manifest.json` → `M1M32:~/dispatch/apps/qc26.json`, clé Anthropic à renseigner dans le manifeste). Après le premier déploiement, la base sur le nœud est la source de vérité : ne pas la réécraser.30