Vrai-Prix — Onglet « Coût » : plan technique d'implantation
Porté d'UQO Éval vers Vrai-Prix le 2026-09-08 (même socle Next 16 ; nœud M3U96a,
~/apps/vrai-prix). Les chemins et noms de processus ci-dessous sont ceux de Vrai-Prix.
Rédigé le 2026-09-06 après audit complet du dépôt. Ce document décrit ce qui existe, ce qui est ajouté, les sources de données réellement accessibles et l'ordre de réalisation. Il est la référence de la fonctionnalité « Méthode du coût » et de l'analyse multimodale des annonces « À vendre ».
1. Audit du dépôt (état au 2026-09-06)
| Aspect | Constat |
|---|---|
| Framework | Next.js 16.3 (App Router, React 19, React Compiler lint), TypeScript strict, Tailwind v4 |
| Backend | Route handlers Node (src/app/api/*), aucune couche serveur séparée |
| Bases | SQLite via better-sqlite3 : data/vraiprix.db (3,75 M unités MAMH + 745 k ventes, src/lib/db.ts), data/immoka.db (70 k annonces à vendre + table vp_eval, src/lib/immoka.ts) |
| UI | Design « terminal éditorial » Vrai-Prix : tokens --accent (rouge signal), classes .kicker .klabel .btn .vp-card .src-table .kv-cell, useLang() FR/EN (LangContext), sections numérotées 01 — |
| Navigation | HeaderNav.tsx (desktop + menu mobile CSS), MobileTabBar.tsx (KaTabbar 6 onglets), FooterNav.tsx, sitemap.ts |
| Traduction | Dictionnaire src/lib/i18n.ts + libellés inline fr ? … : … dans les composants |
pdfkit (src/lib/report*.ts), polices dans assets/fonts, logo Vrai-Prix, format Lettre |
|
| Propriété | getUnit(id) → UnitRow (aire_etages_m2, nb_etages, année, valeur_terrain, valeur_batiment, valeur_role, lat/lng, municipalite, genre_construction, lien_physique, cubf) ; estimateByUnitId → hédonique + comparables |
| Annonces | getListing(uid) → photos (images JSON), description, details JSON, caractéristiques, eval.unit_id (jumelage MAMH), vp_eval (est, model_est, comps_est, cost_est calibré, role_est) |
| Ka | src/lib/ka/tools.ts (KA_TOOLS + runKaTool), boucle agentique SSE dans src/app/api/ka/route.ts (Haiku 4.5) |
| Anthropic | @anthropic-ai/sdk déjà installé, ANTHROPIC_API_KEY dans .env.local (laptop et nœud) |
| Tests | vitest (src/**/*.test.ts), moteur d'estimation testé |
| Déploiement | Source laptop ~/Desktop/vrai-prix → rsync staging M1M32 → mld deploy vrai-prix --node M2U64 (hook post_sync npm run build), PM2 vrai-prix :8150, ngrok www.vrai-prix.app |
Aucune table de coûts, aucun connecteur web, aucun vector store n'existait.
2. Sources externes — faisabilité vérifiée (tests du 2026-09-06)
| Source | Accès testé | Statut licence retenu | Rôle |
|---|---|---|---|
| APCHQ — grilles « Coût horaire de la main-d'œuvre » (PDF publics, media.apchq.com, secteur résidentiel léger/lourd, 2023 → 26 avril 2026) | Firecrawl parse le PDF : taux horaire, vacances 13 %, avantages sociaux, AE, RQAP, FSS, CCQ, CNESST, total employeur par métier | public_open (documents publics de l'association, métadonnées + valeurs numériques, URL conservée) |
Source principale main-d'œuvre (coût employeur complet, historisé) |
| CCQ — salaires conventionnés | Site en incident de sécurité (services fermés jusqu'au 2026-09-08) ; page « Salaire et taux » accessible mais l'outil de taux est JS/indisponible | public_open |
Connecteur implanté (détection de changement par hash), synchro reprendra à la remise en service ; APCHQ reflète déjà les conventions CCQ |
| Statistique Canada — 18-10-0289-01 Indices des prix de la construction de bâtiments (Montréal, Québec RMR ; résidentiel, maison individuelle, rangée, appartements ; 23 divisions), trimestriel, dernier point 2026-T2 | API WDS ouverte (getDataFromCubePidCoordAndLatestNPeriods) |
public_open (licence ouverte StatCan) |
Indexation temporelle, tableau de bord « Coût aujourd'hui », validation macro |
| Canac (canac.ca) | Firecrawl : métadonnée product:price:amount + unité (« / Chaque ») |
public_open (prix affichés publiquement, robots.txt permissif) |
Prix matériaux |
| BMR (bmr.ca) | Firecrawl : product:price:amount (attention aux variantes « à partir de ») |
public_open (URL SEO permises) |
Prix matériaux |
| Patrick Morin | Firecrawl : product:price:amount |
public_open |
Prix matériaux |
| RONA | Cloudflare ; URL FR devinée → 404, l'URL EN de la recherche existe | public_restricted (à valider par connecteur, faible priorité) |
Prix matériaux (si stable) |
| Home Depot Canada | Page servie, mais magasin par défaut hors Québec (Niagara Falls) → prix non localisés | public_restricted |
Désactivé par défaut |
| Altus Group — Canadian Cost Guide 2026 | PDF derrière formulaire (courriel requis) | public_restricted → import manuel après téléchargement légal |
Benchmark $/pi² (import CSV admin) |
| RSMeans / Gordian | Licence payante | licensed / manual_import |
Modèle prévu, import CSV/XLSX autorisé seulement |
Vrai-Prix (interne) — coût calibré du duel des méthodes (marche-stats.json, vp_eval.cost_est) |
Local | interne | Benchmark de cohérence (« Trois lectures de la valeur ») |
Règle : aucun prix n'est inventé. Les quantités d'assemblage, heures de
productivité et pourcentages par défaut sont des hypothèses documentées
(source_type = assumption, badge « hypothèse ») modifiables par l'utilisateur ;
les prix de référence internes pour les articles sans observation détaillant
(béton livré, fermes, fenêtres…) sont étiquetés internal_reference et abaissent
la couverture/confiance. L'interface affiche « Donnée non disponible » plutôt
qu'un nombre fictif.
3. Architecture ajoutée
src/lib/cost/
types.ts types partagés (CostInput, CostEstimate, lignes, provenance)
units.ts conversion d'unités (source_unit → canonical_unit, facteur conservé)
taxonomy.ts divisions MasterFormat, catégories UX, qualités, conditions, matériaux
db.ts data/cost.db (schéma §4, migrations versionnées, seeds idempotents)
seed/ sources, localisations, articles (~130), assemblages (~55), règles
pricing.ts prix canonique par article (médiane robuste multi-source, outliers, indexation)
catalog.ts accès DB : articles + prix courants, assemblages, taux, facteurs, indices, benchmarks
geometry.ts moteur de quantités (empreinte, périmètre, murs, toit, fondations…)
engine.ts calcul PUR et déterministe : assemblages → direct → indirect → RCN → dépréciation → valeur
estimate.ts service : entrée (propriété MAMH / construction / annonce) → estimation sauvegardée
connectors/ firecrawl.ts (client), apchq, ccq, statcan, canac, bmr, patrickmorin, rona, homedepot, altus, rsmeans, matching
ai/ analyse multimodale des annonces (schéma JSON, prompt, images, vecteur, mapping)
src/app/cout/ landing + atelier (/cout), /cout/assemblage/[code], /cout/admin
src/app/api/cost/* API publique §6
src/app/api/admin/cost/* synchro, connecteurs, anomalies, mappings (jeton COST_ADMIN_TOKEN)
src/app/a-vendre/[uid]/analyse-cout analyse IA d'une annonce
src/app/api/listings/[uid]/* analyse IA, estimation, overrides, similaires
scripts/cost-sync.ts planificateur (PM2 cron sur le nœud) : connecteurs → observations → prix canoniques
prompts/property-cost-analysis-v1.mdFirecrawl n'est jamais appelé dans le chemin utilisateur : l'UI lit cost.db.
4. Schéma data/cost.db
Tables (toutes avec created_at) : cost_sources (license_status), cost_raw_observations
(historisées, content_hash, parser_version, status), cost_items,
cost_item_sources (URL produit par détaillant, quantité par emballage),
cost_item_prices (série temporelle canonique : matériau/main-d'œuvre/équipement,
low/median/high, confiance, price_kind observed|derived|indexed|reference),
labour_rates (métier, secteur, base, vacances, avantages, cotisations, total employeur,
effective_from/to, source), cost_locations (facteurs matériau/main-d'œuvre/équipement),
construction_cost_indices, cost_benchmarks, cost_assemblies, cost_assembly_components,
cost_estimates (+ assumptions_json, versions méthode/base/assemblages, cost_snapshot_date),
cost_estimate_lines (calculation_json), cost_estimate_snapshots,
component_condition_rules, connector_runs (observabilité), product_mappings
(matching IA à valider), listing_ai_analyses, listing_ai_overrides, listing_images,
property_embeddings, analysis_conflicts, ai_usage, cost_meta.
5. Moteur (déterministe)
matériau_i = Q × qté_i × (1 + pertes_i) × prix_i × FacteurMatériau
main-d'œuvre = Q × heures_i × taux_métier_i × FacteurMainDœuvre
équipement = Q × équip_i × taux_équip × FacteurÉquipement
Direct = Σ (par assemblage, regroupé en catégories UX)
Indirects = Direct × Σ pct (plans, permis, assurances, gestion, financement…)
Frais gén. = (Direct + Indirects) × pct ; Profit = (… + FG) × pct ; Contingence = (Direct + Indirects) × pct
RCN = Direct + Indirects + FG + Profit + Contingence
D_phys = âge-vie (âge effectif / vie économique) OU par composante (vie propre + condition → âge effectif)
V_coût = V_terrain + RCN − (D_phys + D_fonct + D_ext)Fourchette : σ par ligne (dispersion observée ou ±8 % référence / ±12 % hypothèse, heures ±15 %) combinés en racine des carrés + composante systématique (facteur régional, modèle d'assemblage 5 %) → P10/P90. Confiance 0-100 (fraîcheur 20, couverture 20, localisation 15, main-d'œuvre 15, benchmarks 10, bâtiment 20) → A-D.
Invariants testés : RCN ≥ Direct, valeur dépréciée ≥ 0, V = terrain + dépréciée,
même entrée + même instantané de prix = même résultat.
6. API
Publique : GET /api/cost/overview, /items, /items/[code], /assemblies,
/assemblies/[code], /labour, /materials/history, /locations, /benchmarks,
/sources, /property?id= (préremplissage MAMH), POST /api/cost/estimate,
GET /api/cost/estimate/[id], GET /api/cost/report?estimate= (PDF).
Admin (jeton) : GET /api/admin/cost/connectors, POST /sync/[connector],
GET /anomalies, GET|POST /mappings, POST /benchmarks/import.
Annonces : POST|GET /api/listings/[uid]/ai-cost-analysis, /reanalyze,
PATCH /override, POST|GET /cost-estimate, GET /technical-similar, GET /ai-analysis-json.
7. Ordre de réalisation
- Audit (fait) · 2. Schéma + seeds · 3. Moteur pur + tests · 4. ≥ 40 assemblages ·
- Main-d'œuvre APCHQ (+ CCQ stub) · 6. StatCan · 7. Détaillants Firecrawl ·
- Benchmarks · 9. UI
/cout· 10. Intégration propriété · 11. Dépréciation · - Sources/audit · 13. PDF · 14. Ka · 15. Admin · 16. Tests, mobile, FR/EN ·
- Analyse multimodale des annonces (Sonnet 5) · 18. Déploiement M2U64 via mld
(cron PM2
vrai-prix-cost-sync,FIRECRAWL_API_KEYcôté serveur).
8. Hors périmètre du MVP (phase 2/3)
Vision à partir de plans, embeddings d'images, 500+ articles, localisation par municipalité fine, import RSMeans réel, entraînement de modèles.