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:
| Service | Reads | Used for |
|---|
decisionVectors | decision_vector scores | enrich triggered alert items at the latest snapshot |
actionVectors | action_vector recommendations | pre-fill plan items, materialize tasks |
decisionDistribution | aggregated decision_vector | urgency 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 — recommended vs final
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