> ## Documentation Index
> Fetch the complete documentation index at: https://docs.solya.app/llms.txt
> Use this file to discover all available pages before exploring further.

# App consumption

> How the Next.js app reads decision and action vectors over Postgres — services, urgency heatmap, score-driven workflows, task materialization, confidence gating, and immutable per-item lineage.

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.

```mermaid theme={null}
flowchart TD
  LB[(gold_lakebase<br/>decision_vector · action_vector)]
  subgraph App["Solya app (read-only analytics schema)"]
    SVC["services<br/>decisionVectors · actionVectors · decisionDistribution"]
    HEAT["Urgency heatmap & chips<br/>URGENCY_THRESHOLDS"]
    WF["Workflows<br/>scoreDriven / recommended · CREATE_TASKS"]
    PRE["Alert workspace pre-fill<br/>+ plan-review reference reco"]
    ITEM["Plan items<br/>decision_vector_snapshot (immutable)"]
    BADGE["LineageBadge<br/>recommended vs final"]
    AUDIT["Activity log<br/>decisionVectorId attribution"]
  end

  LB --> SVC
  SVC --> HEAT
  SVC --> WF
  SVC --> PRE
  WF --> ITEM
  PRE --> ITEM
  ITEM --> BADGE
  ITEM --> AUDIT
```

## 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:

```ts theme={null}
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](/en/developers/workflows/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:

```json theme={null}
{ "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.

<Warning>
  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.
</Warning>

### 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](/en/inventory-plans/lifecycle#activity--audit-trail). 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`
