gold.decision_context— one consolidated snapshot row per position (stock, margin, forecast, dimension keys).gold.decision_vector— per-domain risk/urgency scores derived from the context (e.g. restock urgency, stockout risk, surplus/deficit, transfer urgency).gold.action_vector— the resolved recommendation (a quantity or a discount) plus an enriched “why” snapshot, derived from the scores.
action_vector (and the underlying decision_vector scores)
over Postgres and uses them to pre-fill plans, materialize tasks, drive score-driven
workflow strategies, and attribute every recommended item back to its source.
The two vectors, in one sentence
- A decision vector answers “how risky / urgent is this position?” — raw scores in
[0, 1], no units. - An action vector answers “so what should we do about it?” — a concrete recommended quantity or discount percentage, with a confidence and an explanation.
Grain — variant × shop, no size axis
Every stage is keyed on(organization_id, variant_id, shop_id, snapshot_date) — and, from
the decision vector onward, a domain axis. There is deliberately no size axis:
decision_context collapses every scoring input across size_taxonomy_id, so recommendations
are variant-level and the app aggregates sizes for display. (Per-size sizing — e.g. the
restock size-curve split — happens later, in the app, when items are added to a plan.)
Daily snapshots and idempotency
Each table is a daily snapshot. Writers useMERGE on the primary key, so:
- re-running a build on the same calendar day updates rows in place (a byte-equal no-op when inputs are unchanged — scoring is deterministic);
- different domains for the same
(variant, shop, snapshot_date)coexist (distinctdomainvalues); - prior daily slices are preserved (
decision_contextkeeps a 90-day retention window).
<env>.gold_lakebase.*) via a
triggered CDC sync, so the app reads them with low-latency Postgres queries at variant /
product / brand grain.
The three decision domains
| Domain | Question | Vector scores | Recommended action |
|---|---|---|---|
restock | Are we about to run out? | restock_urgency, stockout_risk, overstock_risk | recommended_qty (units to reorder) |
rebalance | Is stock in the wrong shop? | surplus_score, deficit_score, transfer_urgency, surplus_units, deficit_units | recommended_qty (units to transfer) |
markdown | Should we discount? | markdown_score + discount fraction (reusing restock slots) | recommended_discount_pct |
Domain values are lowercase everywhere in the gold layer (
"restock", "rebalance",
"markdown"). The app mirrors them in the DecisionDomain constant.Read next
Decision context
The foundational input table — its sources, columns, and the v1 always-NULL slots.
Decision vector
The scoring layer — per-domain formulas, weights, and the action gate.
Action vector
The resolution layer — how scores become quantities and discounts, with confidence.
App consumption
How the Next.js app reads, gates, attributes, and acts on the vectors.

