Audits · septembre 2026Audit — Spy plateforme ↔ extension BoostEcom (2026-09-10)

Audit — Spy plateforme ↔ extension BoostEcom (2026-09-10)

Contrat lu cote extension : GET /api/intelligence/lookup?q=…&rich=1 (plus hub/scan, similar?enrich=1, graph, extras). Ce document ne dit pas que l'extension ignore ads/traffic. Les empty states live observes (= KPI mois…

Contrat lu cote extension : GET /api/intelligence/lookup?q=…&rich=1 (plus hub/scan, similar?enrich=1, graph, extras). Ce document ne dit pas que l'extension ignore ads/traffic. Les empty states live observes (= KPI mois present, visits_series: null, ads_absence renseigne — souvent not_observed ou quarantined — screenshot_url present) matchent le code + l'env, pas un trou de projecteur.

Symboles cites : projectVisitsSeries, leafAbsence, projectTopProducts, HubStore, buildEmission (meta-ad-library).


A → E (code)

A. visits_series

StatutJuste env / historique manquant (projecteur OK)
  • projectVisitsSeries existe (src/services/algorithms/intelligence/hub-projection.ts) et exige ≥ 2 points {visits, month} ; sinon null.
  • Le scrape public SimilarWeb remplit monthly_visits (+ souvent visits_change_pct) mais renvoie une serie vide / un seul mois → emission absent("not_observed") quand un KPI existe sans courbe (probes/traffic-provider.ts).
  • La courbe multi-mois arrive des adaptateurs cles : SIMILARWEB_API_KEY / SEMRUSH_API_KEY / DATOS_API_KEY (docs/ops/intelligence-env-matrix.md §4).
  • Verdict courbe vide : keys only (ou panel sous plancher = traffic.no_coverage), pas bug de projection.

B. ads_absence : not_observed vs quarantined

StatutDeja fait (semantique types) + Partiel (zero mesure)
  • Definitions : src/services/algorithms/intelligence/canonical/types.ts (ABSENCE_REASONS) :
    • not_observed : aucun L2 n'a pose de valeur (ou le producteur n'a rien emis).
    • quarantined : une valeur observee a ete rejetee comme invraisemblable ; projectAdsSeries droppe alors l'historique disowned (soundAdSnapshots + canonicalQuarantined).
  • Projection : ads_absence: leafAbsence(rec.ads?.active_creatives).
  • Piege : buildEmission dans probes/meta-ad-library.ts fait if (agg.active_creatives === 0) return {}. Un scrape Meta complete a zero ne pose donc pas present(0) ; le template reste not_observed. L'UI extension qui attend active_creatives === 0 pour « mesure a zero » ne le verra pas tant que ce chemin n'emet pas 0.
  • Sans META_AD_LIBRARY_TOKEN / Apify : pas d'ingest creatives → not_observed + creatives: [] est le resultat attendu, pas une faute de rich=1.

C. 0583 + #1001 — top_products enrichis

StatutDeja fait (merge origin/main)
  • Merge PR #1001 (1f75ade). Catalog probe + projectTopProducts passent published_at / compare_at_price / tags quand le feed /products.json les porte (hub-projection-top-products.test.ts).
  • Residual : un store doit etre rescanné apres le deploy pour que le JSON canonique porte les nouveaux champs. Pas un trou d'API.

D. 0584 + #1002 — ContextIQ → BoostEcom Spy

StatutDeja fait (code + live privacy)
  • Merge PR #1002 (44ac65b). Messages / config produit sans string user-facing « ContextIQ » (compat clipboard + tokens de contrat data-contextiq / slug CWS conserves volontairement).
  • Vérification en direct de /legal/privacy (2026-09-10) : copy « BoostEcom Spy ».

E. screenshot_url

StatutDeja fait
  • Projete dans HubStore.screenshot_url depuis identity.screenshot_url (masque si ANONYMIZED).
  • Inclus dans searchHubStores / lookup?rich=1.

B. Verdicts une phrase

  1. Courbes trafic vides → keys only (ou historique < 2 mois), projecteur branche.
  2. Ads quarantined → policy corpus (lecture disowned) ; Meta public CTA extension = compensation, pas re-ecriture HubStore.
  3. Creatives vides alors que la Ads Library publique a des pubs → ingest / token Meta-Apify manquant, ou ownership filter, ou zero non emis ; pas un oubli rich=1.
  4. Enrichissement top_products 0583 → merge main ; live apres rescan catalogue.
  5. Etat de l'art 2026 → socle API OK ; edge = cles trafic + job Meta + emit zero honnete.

C. Plan action

PrioriteActionPropriétaire
P0 opsPoser SIMILARWEB_API_KEY ou SEMRUSH_API_KEY (prod) ; preuve = 1 domaine PUBLIC avec visits_series.length >= 2 via lookup?rich=1operateur
P0 opsPoser META_AD_LIBRARY_TOKEN (et/ou Apify) ; relancer scans HOToperateur
P0 docsCette note + unites MoM/ratio + matrice env (cette PR)intelligence
P1 codebuildEmission : sur status complete et 0 owned, emettre present(0) pour distinguer mesure vide / jamais vuintelligence (item suivi)
P1Job Ads Library robuste (cron plateforme, pas MV3 CWS)intelligence
P2Endpoint refresh ads/traffic par domaine pour session extension — interdit : 4e prefixe credential (ADR 0007), POST Origin chrome-extension://security + intelligence

D. Contrat API stable (snake, HubStore)

Confirme pour l'extension (deja projetes) :

monthly_visits, visits_change_pct (RATIO, ex. -0.199),
visits_series: [{ t: "YYYY-MM"|null, v: number }] | null,
mom_growth_pct (POINTS, ex. -19.9 — NE PAS ×100),
country_breakdown, top_source, paid_share,
active_creatives, ads_absence, ads_series: [{ t, v }] | null,
ad_networks, creatives[],
top_products: { handle, title, image_url, price?, published_at?,
                compare_at_price?, tags? }[],
screenshot_url, screenshot_at,
niche, categories, rating, total_reviews, revenue_eur, aov_eur, …

lookup?rich=1 → { suggestions, stores: HubStore[] }.

traffic_collection (octobre 2026)

Pour un store SANS chiffre de trafic, le lookup dit lui-meme pourquoi, sans POST hub/scan (rendus payants, slot par domaine, connexion) :

"traffic_collection": {
  "state": "measured" | "no_coverage" | "never",
  "checked_at": "2026-09-20T07:30:00.000Z" | null
}
  • measured : un chiffre de visites a ete stocke (date = son observation). checked_at peut etre null pour un chiffre stocke avant que les chiffres soient dates.
  • no_coverage : la source a repondu qu'elle ne suit pas ce site (SimilarWeb, HTTP 404). Autorise « la source de trafic ne couvre pas ce store » avec la date checked_at. Si un ancien chiffre existe encore, il reste affiche avec sa propre date.
  • never : aucune collecte reelle enregistree ; checked_at: null. Autorise seulement « pas encore collecte », jamais un zero.

Etat de la DERNIERE collecte qui a reellement atteint sa source, quelle que soit la requete ou le cron qui l'a lancee (scan hub, refresh-runner, crons, scans admin / agents) : une tentative bloquee, en erreur ou sautee (cache frais) n'ecrase jamais un etat precedent. Ecrit par le coordinateur (traffic-collection.ts) dans record.traffic.collection, sans colonne ni migration ; sans backfill (un chiffre date deja stocke se lit measured a la lecture).

Gratuit sur tous les plans (il dit si un chiffre existe, jamais le chiffre) ; absent pour un store ANONYMIZED, et un store masque ou supprime n'est jamais servi. Sur hub/scan, store.traffic_collection cohabite avec enrichment / enrichment_at, qui restent « ce que CETTE requete a collecte ». similar?enrich=1 → peers avec projection.

Deep-links : /intelligence/top-stores/{domain}, /intelligence/inspect/{theme|apps|ads|emails}?domain=… (pas /intelligence/inspect/catalog).


E. Procedure de verification (extension / QA)

Sans citer de marque boutique :

# 1. Shape rich
curl -sS "https://www.boostecom.app/api/intelligence/lookup?q={domain}&rich=1&limit=1" \
  | jq '{n:(.stores|length),
         visits:(.stores[0].monthly_visits),
         series:(.stores[0].visits_series|length?),
         change:(.stores[0].visits_change_pct),
         mom:(.stores[0].mom_growth_pct),
         ads_absence:(.stores[0].ads_absence),
         active:(.stores[0].active_creatives),
         creatives:(.stores[0].creatives|length),
         shot:(.stores[0].screenshot_url!=null),
         top_keys:(.stores[0].top_products[0]|keys)}'

# 2. Preuve trafic API = series >= 2 (sinon cles / coverage, pas l'extension)
# 3. Legal
curl -sS https://www.boostecom.app/legal/privacy | grep -o 'BoostEcom Spy' | head -1

Unites a tester cote UI : meme magasin avec visits_change_pct = -0.199 et mom_growth_pct = -19.9 doivent afficher tous les deux « −19,9 % », jamais −0,2 % / −1990 %.


Bloqueurs (une ligne)

  1. Secrets trafic prod : sans SimilarWeb/Semrush/Datos, visits_series reste null meme si le KPI mois est rempli.
  2. Secrets Meta/Apify : sans token, creatives + active_creatives restent absents (not_observed).
  3. Zero ads non emis : buildEmission({}) sur 0 confond « mesure a zero » et « jamais observe ».
  4. Rescan catalogue : champs 0583 absents sur records pre-merge jusqu'au prochain deep scan.

Preuve live 2026-09-10 (lookup?rich=1)

Sans citer d'autres marques : sur le domaine de reference de la matrice env (§9) :

ChampValeurNote
monthly_visitspresentscrape gratuit OK
visits_seriesnullpas de preuve courbe
visits_change_pctratio (ex. −0.22)ne pas afficher brut
mom_growth_pctpoints % (ex. −6.7)preferer si present
ads_absence"quarantined"≠ silence
creatives[]pas d'ads observes
top_products[0].published_at / compare_at_price / tagspresents0583 deja en prod sur cette fiche
screenshot_urltruthyOK

→ Extension : wait corpus pour (1)(2). Produits : retest possible sans batch rescan sur cette fiche.