Skip to main content
The data platform writes gold.decision_vector and gold.action_vector, mirrors them to Lakebase Postgres, and the Solya app reads them there. This page covers the app side: how those vectors are read, gated, displayed, attributed, and turned into action.

Read path — analytics schema + services

The gold tables are exposed as read-only Drizzle schemas under src/server/database/analytics/schema/gold/ (decision-vector.ts, action-vector.ts), with column constants in src/constants/datasetColumns/gold/. Three services wrap them:
ServiceReadsUsed for
decisionVectorsdecision_vector scoresenrich triggered alert items at the latest snapshot
actionVectorsaction_vector recommendationspre-fill plan items, materialize tasks
decisionDistributionaggregated decision_vectorurgency stats at variant / product / brand grain
The DecisionDomain constant, the scores JSONB keys, and the urgency thresholds live in src/constants/decisionDistribution.ts — a single source of truth shared by the SQL orchestrator and the UI helpers.

Urgency thresholds & heatmap

URGENCY_THRESHOLDS maps raw scores to “needs action” flags, chip colours, and heatmap tiers:
URGENT:           0.5            // flags a (variant, shop) as needing action
CHIP_WARNING:     0.3
CHIP_ERROR:       0.6
TIER_BREAKPOINTS: [0.2, 0.4, 0.6, 0.8]   // 5-band heatmap
decisionDistribution rolls these up — e.g. shopsInRestockZone counts shops with restock_urgency ≥ 0.5, and the brand summary surfaces a cross-domain urgentCount.

Score-driven workflows

Workflow ADD_ITEMS_TO_PLAN strategies route to the decision layer (src/types/workflows.ts):
  • scoreDriven (quantity / matching) — the data platform’s resolver reads the decision vector to set the quantity or to match rebalance shops. No app-side parameters.
  • recommended (markdown) — decision-layer discount with three bounded knobs (fallbackPercent 15, maxDiscountPct 70, marginFloorPct 0).
See Workflows — plan actions for the full config.

CREATE_TASKS — materializing the action vector into the inbox

The CREATE_TASKS workflow action turns action_vector rows into PRODUCT_ACTION tasks, scoped to the trigger’s matched (variantId, shopId) pairs:
{ "actionType": "CREATE_TASKS", "domains": ["restock", "markdown"], "minConfidence": 0.7 }
  • domains — a non-empty subset of ["restock", "rebalance", "markdown"].
  • minConfidence — rows below this confidence are suppressed; rows with null confidence (e.g. restock) are always kept.

Confidence gating on read

getActionVectorsForItemsAction (alert-workspace rich tier, plan-review reference reco) applies the org-global confidence threshold at read time: a row whose confidence is below the threshold is returned with resolved: false, so the UI treats it as “no recommendation — enter manually” rather than pre-filling a low-confidence value. The plan-review hook usePlanReviewReferenceReco fetches action_vector rows (deduplicated, capped at 200 pairs) only for lines that have no stored snapshot — so review screens can show a reference recommendation even for manually-added lines.

Immutable per-item lineage

When an item is emitted into a plan from a recommendation, the strategy’s decisionVectorSnapshot is captured at emit time and stored immutably on the plan item (*_plan_items.decision_vector_snapshot, plus applied_rules). The shape is PlanItemDecisionVectorSnapshot (src/types/planItemLineage.ts): capturedAt, strategyId / strategyVersion, confidence, markdown-only markdownScore / agedStockFlag, and a snapshot_date fallback. It is .passthrough() for forward-compatibility.
The snapshot is immutable — it records the original strategy decision. Subsequent quantity / discount edits must never overwrite it. Manually-added items carry a NULL snapshot.
LineageBadge (src/components/shared/chips/LineageBadge.tsx) surfaces the difference between what the strategy recommended and what was finally persisted (after OTB / budget / supplier constraints). It renders nothing when there is no recommendation and no applied rules, or when the recommendation equals the final value with no constraint deltas — otherwise it shows the recommended value with a tooltip of the applied rules and strategy id.

Audit attribution

Every recommended item is attributed in the plan’s activity log. A DECISION_VECTOR attribution carries a decisionVectorId linking back to the snapshot; MANUAL and WORKFLOW_FALLBACK carry no snapshot reference (src/constants/planAuditLog.ts).

Source (app)

  • Analytics schema: src/server/database/analytics/schema/gold/{decision-vector.ts, action-vector.ts}
  • Services: src/server/services/{decisionVectors, actionVectors, decisionDistribution}/
  • Constants: src/constants/decisionDistribution.ts, src/constants/taskConfidence.ts, src/constants/planAuditLog.ts
  • Lineage: src/types/planItemLineage.ts, src/components/shared/chips/LineageBadge.tsx
  • Workflows: src/types/workflows.ts