gold.decision_vector is the scoring layer. It reads decision_context
and emits one row per (organization_id, variant_id, shop_id, snapshot_date, domain) carrying
a risk vector — scores in [0, 1] — plus the action gate (allowed_actions /
forbidden_actions) and an audit trail (applied_rules).
variant_id / shop_id are NOT NULL here (the MERGE key requires it), even though they are
nullable upstream on decision_context.
The scores STRUCT
A single NOT NULL STRUCT carries every domain’s score fields side-by-side. Sub-fields are
nullable so a row populated by one domain leaves the others’ sub-fields NULL — keeping the
table single-shape across domains. Non-nullness for the populated domain is guaranteed by
construction: every NULL upstream input is coalesced to a neutral default before scoring.
The action gate
Each row also carries threeNOT NULL arrays:
allowed_actions— baseline emits the domain itself, e.g.["restock"].forbidden_actions—[]in v1.1; future SOURCING / SIZING rules populate it.applied_rules— audit trail, always non-empty (size(applied_rules) > 0). The first element is a domain qualifier (e.g.markdown_domain_qualifier:v1); subsequent elements are the IDs of any scoring business-logic rules that fired.
Scoring by domain
- Restock
- Rebalance
- Markdown
Source: NULL defaults:
build_decision_vector/scoring.py. All weights are module constants.restock_urgency ∈ [0, 1]stockout_risk ∈ [0, 1]overstock_risk ∈ [0, 1]days_of_cover → 30 (neutral), forecast_30d → 0 (no demand),
gross_margin_pct → 0, aged_stock_flag → false. An all-NULL row scores (0, 0, 0).Validation
- Errors (fail the task):
decision_vector_not_empty,decision_vector_required_fields,decision_vector_pk_unique,decision_vector_applied_rules_non_empty,decision_vector_domain_allowed_v11, and NULL-safeBETWEEN 0 AND 1bounds on the rebalance scores (surplus_score,deficit_score,transfer_urgency). - Warnings: range checks on the restock scores (
restock_urgency,stockout_risk,overstock_risk).
Scoring weights are module-level constants today; tuning is a reviewed PR, not a settings
knob. A future calibration path may surface them via gold settings once production data
shows it is needed.
Source
- Schema:
pipelines/shared/schemas/gold/decision_vector.py - Scoring:
build_decision_vector/{scoring.py, scoring_markdown.py},build_decision_vector_rebalance/scoring_rebalance.py - Markdown lookup:
pipelines/shared/config/markdown_discount_lookup.yaml - Repo doc:
docs/gold/decision-vector.md

