Skip to main content
gold.decision_context is the foundation of the decision layer. It materializes one row per (organization_id, variant_id, shop_id, snapshot_date), consolidating stock state, margin, forecast, and dimension keys into a single shape that every decision-vector scorer reads. Build it once, score it three ways.
ConceptValue
Grainone row per (organization_id, variant_id, shop_id, snapshot_date)
Primary key["organization_id", "variant_id", "shop_id", "snapshot_date"]
Write modeMERGE on the PK (idempotent same-day reruns)
Clustering(organization_id, snapshot_date, variant_id)
Retention90 days of daily snapshots per position (writer-enforced)
WriterBuildDecisionContextTask (pipelines/layers/gold/tasks/build_decision_context/)

Lineage — three gold inputs

The builder anchors on gold.stock_snapshot (one position = one stock row) and LEFT-joins margin and forecast onto it, collapsing every input across size_taxonomy_id first.
  • stock_snapshot is the anchor: no positions → nothing to build (skip_task_if_empty).
  • sales_kpis supplies gross_margin_pct (LEFT JOIN on the position key).
  • sales_forecasts is best-effort — filtered to granularity = 'daily', summed over the next-30-day window; a missing forecast leaves the column NULL.

Column reference

Audit columns (created_at, updated_at) are omitted. The aged_stock_flag is the only non-position column that is NOT NULL.

Stock state

ColumnTypeMeaningSource / transformation
current_stockDOUBLEOn-hand quantity at snapshot_datestock_snapshot.current_stock_qty, SUM across sizes
target_stockDOUBLETarget stock for the positionv1: always NULL (Phase-2 policy-table hook)
days_of_coverDOUBLEDays of cover at current pacestock_snapshot.days_of_supply, MIN across sizes
lead_time_daysINTSupplier lead timev1: always NULL (lands with the supplier dimension)

Sales & margin

ColumnTypeMeaningSource / transformation
gross_margin_pctDOUBLERealised gross margin %LEFT JOIN sales_kpis
sell_through_7d / 30d / 90dDOUBLESell-through ratesv1: always NULL (per-position computation is Phase 2)
aged_stock_flagBOOLEANAged-stock indicator (NOT NULL)(snapshot_date − last_sale_date) ≥ aged_stock_threshold_days (default 90); false on parse failure

Forecast

ColumnTypeMeaningSource / transformation
forecast_30dDOUBLE30-day demand forecastSUM sales_forecasts.quantity over (snapshot_date, +30]
forecast_confidenceDOUBLEConfidence of the 30-day forecastAVG forecast_confidence over the same window

Dimension keys (for rule-scope matching)

ColumnTypeMeaningSource
brand_idSTRINGBrand (denormalised)passthrough from the anchor stock_snapshot row
category_idSTRINGCategoryv1: always NULL
supplier_idSTRINGSupplierv1: always NULL

v1 always-NULL slots

Seven columns are typed and reserved but always NULL in v1target_stock, lead_time_days, sell_through_7d/30d/90d, category_id, supplier_id. Their type and nullability are stable so the writer can fill them later without a DDL migration.
Because target_stock is always NULL in v1, the rebalance surplus/deficit ratios collapse to 0 across the catalogue (a position with no target genuinely cannot be classified as surplus or deficit). This is correct, deterministic behavior — real rebalance scores land the day target_stock is populated. See the decision vector page.
The scoring layer treats NULL as “skip this signal for this position” by coalescing each NULL input to a neutral default before the math runs (documented per-domain on the next page).

Validation

Rules are embedded on the schema and applied by the writer:
  • Errors (fail the task): decision_context_not_empty, decision_context_required_fields (organization_id, snapshot_date, aged_stock_flag non-NULL), decision_context_pk_unique.
  • Warnings: non-negativity checks on current_stock, days_of_cover, forecast_30d.
A DecisionContextHealthTask raises [freshness] (no recent snapshot) or [row-count-drift] (today’s slice diverges from yesterday’s) breaches for on-call triage.

Source

  • Schema: pipelines/shared/schemas/gold/decision_context.py
  • Writer: pipelines/layers/gold/tasks/build_decision_context/task.py
  • Repo doc: docs/gold/decision-context.md