QC26 — repères pour le développement
Spé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.
Règles non négociables
- 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.
- Les couleurs et logos des partis viennent uniquement de
engine/qc26/parties.pyetweb/src/lib/parties.ts; jamais de hex dans un composant. Logos officiels dansweb/public/parties/<id>/avecsource.json; ne pas les recréer ni recolorer (le PCQ, logo blanc, se pose sur sa pastille marine). - Le nombre de sièges (127) et le seuil (64) sont calculés depuis la liste DGEQ (
settingsseats_total/majority_threshold), jamais codés. - 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é. - Toute correction humaine passe par
/api/admin/*avec une raison →audit_logs.
Où sont les choses
- Moteur :
engine/(FastAPI :8201). Base SQLiteengine/data/qc26.db(WAL). Archivesengine/data/archive/{polls,documents,live}par SHA-256. Copies officielles DGEQengine/data/official/. - Web :
web/(Next 16, :8200), rewrite/api/*→ moteur. Composants du design system dansweb/src/components/. - 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 = bumpmodel_version+ entréeCHANGELOGdansapi/public.py. - 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). Tablessignals,markets,market_prices,media_items,by_elections. API/api/signals*,/api/forecast/components, adminPOST /api/admin/signals/refresh?what=all. Page/signaux. - Design : tokens clair/sombre dans
web/src/app/globals.css(data-theme, script inlineTHEME_INIT_SCRIPT), Fraunces (display) + Inter + JetBrains Mono ; couleurs de partis viapartyVar()/partyAlpha()(variables CSS--p-*, éclaircies en sombre) —partyColor()(hex) réservé aux images OG. - Soirée électorale :
engine/qc26/live/(feed officiel, simulation 2022, nowcast).POST /api/admin/live/simulation/startrejoue 2022 en accéléré pour tester la chaîne complète.
Gotchas
- SQLite : ne jamais tenir une transaction ouverte pendant un appel LLM (commits explicites dans les connecteurs) ; timeout 90 s. Les
DateTimepassent parTZDateTime(UTC explicite à la relecture) : toujours.isoformat()sur des objets tz-aware. - 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). - Sortant·e = drapeau
depute_sortantde la liste DGEQ des candidatures, puis gagnant de partielle, puis gagnant 2022 transposé (jamais l'inverse). - Fraunces via
next/font/google:axesseulement sansweight(police variable). - DGEQ : « PCQ » = Parti communiste, le Parti conservateur est « PCOQ » ; CSV en cp1252 avec tiret 0x96 (
util.fix_dgeq_dash). - Wikipédia n'est qu'un index d'amorçage (tier 5) : les lignes
secondary-indexsont 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. - La règle « pas de candidature → part résiduelle » ne s'active que pour un parti dont la liste est ≥ 90 % complète.
- Next :
useSearchParamsinterdit en prerender (passersearchParamsdu serveur) ; images OG satori =display:flexsur tout div à plusieurs enfants. - Déploiement :
mld stage ~/Desktop/Projets/apps-web/qc26 qc26 && mld deploy qc26(manifestedeploy/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.