SPB Git forge

spb/qc26

Public
10commits 1branches 0releases
2.8 MBsize
maindefault branch
17 days agolast push
Python 51.6% TypeScript 46.7% CSS 1.7%
4.7 KB

# 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.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).
  • 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.
  • 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 SQLite engine/data/qc26.db (WAL). Archives engine/data/archive/{polls,documents,live} par SHA-256. Copies officielles DGEQ engine/data/official/.
  • Web : web/ (Next 16, :8200), rewrite /api/* → moteur. Composants du design system dans web/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 = bump model_version + entrée CHANGELOG dans api/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). 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.
  • 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.
  • 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.

# Gotchas

  • 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.
  • 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_sortant de la liste DGEQ des candidatures, puis gagnant de partielle, puis gagnant 2022 transposé (jamais l'inverse).
  • Fraunces via next/font/google : axes seulement sans weight (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-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.
  • La règle « pas de candidature → part résiduelle » ne s'active que pour un parti dont la liste est ≥ 90 % complète.
  • Next : useSearchParams interdit en prerender (passer searchParams du serveur) ; images OG satori = display:flex sur tout div à plusieurs enfants.
  • Déploiement : mld stage ~/Desktop/Projets/apps-web/qc26 qc26 && mld deploy qc26 (manifeste deploy/qc26.manifest.jsonM1M32:~/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.