La data platform écrit gold.decision_vector et gold.action_vector, les réplique vers
Lakebase Postgres, et l’app Solya les y lit. Cette page couvre le côté app : comment ces
vecteurs sont lus, filtrés, affichés, attribués et transformés en action.
Chemin de lecture — schéma analytics + services
Les tables gold sont exposées en schémas Drizzle en lecture seule sous
src/server/database/analytics/schema/gold/ (decision-vector.ts, action-vector.ts), avec
les constantes de colonnes dans src/constants/datasetColumns/gold/. Trois services les
encapsulent :
| Service | Lit | Sert à |
|---|
decisionVectors | scores decision_vector | enrichir les articles d’alerte déclenchés au dernier snapshot |
actionVectors | recommandations action_vector | pré-remplir les articles de plan, matérialiser les tâches |
decisionDistribution | decision_vector agrégé | stats d’urgence au grain variante / produit / marque |
La constante DecisionDomain, les clés JSONB scores et les seuils d’urgence vivent dans
src/constants/decisionDistribution.ts — une source de vérité unique partagée par
l’orchestrateur SQL et les helpers d’UI.
Seuils d’urgence & heatmap
URGENCY_THRESHOLDS mappe les scores bruts en drapeaux « action requise », couleurs de chips
et paliers de heatmap :
URGENT: 0.5 // marque un (variante, magasin) comme nécessitant une action
CHIP_WARNING: 0.3
CHIP_ERROR: 0.6
TIER_BREAKPOINTS: [0.2, 0.4, 0.6, 0.8] // heatmap à 5 bandes
decisionDistribution les agrège — p. ex. shopsInRestockZone compte les magasins avec
restock_urgency ≥ 0.5, et le résumé marque surface un urgentCount inter-domaines.
Workflows pilotés par score
Les stratégies ADD_ITEMS_TO_PLAN des workflows routent vers la couche décision
(src/types/workflows.ts) :
scoreDriven (quantité / matching) — le resolver de la data platform lit le vecteur de
décision pour fixer la quantité ou apparier les magasins de rebalance. Aucun paramètre côté app.
recommended (markdown) — remise de la couche décision avec trois bornes
(fallbackPercent 15, maxDiscountPct 70, marginFloorPct 0).
Voir Workflows — actions de plan pour la config complète.
CREATE_TASKS — matérialiser le vecteur d’action dans la boîte de réception
L’action de workflow CREATE_TASKS transforme les lignes action_vector en tâches
PRODUCT_ACTION, scopées aux paires (variantId, shopId) matchées par le déclencheur :
{ "actionType": "CREATE_TASKS", "domains": ["restock", "markdown"], "minConfidence": 0.7 }
domains — un sous-ensemble non vide de ["restock", "rebalance", "markdown"].
minConfidence — les lignes sous ce seuil sont supprimées ; les lignes à confiance
null (p. ex. restock) sont toujours conservées.
Filtrage par confiance à la lecture
getActionVectorsForItemsAction (palier riche de l’espace alertes, reco de référence en revue
de plan) applique le seuil de confiance global de l’org à la lecture : une ligne dont la
confiance est sous le seuil est renvoyée avec resolved: false, pour que l’UI la traite comme
« pas de recommandation — saisie manuelle » plutôt que de pré-remplir une valeur peu fiable.
Le hook de revue de plan usePlanReviewReferenceReco récupère des lignes action_vector
(dédupliquées, plafonnées à 200 paires) uniquement pour les lignes sans snapshot stocké —
afin que les écrans de revue affichent une recommandation de référence même pour les lignes
ajoutées manuellement.
Lignage immuable par article
Quand un article est émis dans un plan depuis une recommandation, le decisionVectorSnapshot
de la stratégie est capturé à l’émission et stocké de façon immuable sur l’article de plan
(*_plan_items.decision_vector_snapshot, plus applied_rules). La forme est
PlanItemDecisionVectorSnapshot (src/types/planItemLineage.ts) : capturedAt, strategyId /
strategyVersion, confidence, markdownScore / agedStockFlag (markdown), et un fallback
snapshot_date. Elle est en .passthrough() pour la compatibilité ascendante.
Le snapshot est immuable — il enregistre la décision originale de la stratégie. Les
éditions ultérieures de quantité / remise ne doivent jamais l’écraser. Les articles ajoutés
manuellement portent un snapshot NULL.
LineageBadge — recommandé vs final
LineageBadge (src/components/shared/chips/LineageBadge.tsx) surface l’écart entre ce que la
stratégie a recommandé et ce qui a finalement été persisté (après contraintes OTB / budget /
fournisseur). Il n’affiche rien quand il n’y a ni recommandation ni règle appliquée, ou quand
la recommandation égale la valeur finale sans delta de contrainte — sinon il montre la valeur
recommandée avec une infobulle des règles appliquées et l’id de stratégie.
Attribution d’audit
Chaque article recommandé est attribué dans le
journal d’activité du plan. Une
attribution DECISION_VECTOR porte un decisionVectorId renvoyant au snapshot ; MANUAL et
WORKFLOW_FALLBACK ne portent aucune référence de snapshot (src/constants/planAuditLog.ts).
Source (app)
- Schéma analytics :
src/server/database/analytics/schema/gold/{decision-vector.ts, action-vector.ts}
- Services :
src/server/services/{decisionVectors, actionVectors, decisionDistribution}/
- Constantes :
src/constants/decisionDistribution.ts, src/constants/taskConfidence.ts, src/constants/planAuditLog.ts
- Lignage :
src/types/planItemLineage.ts, src/components/shared/chips/LineageBadge.tsx
- Workflows :
src/types/workflows.ts