Skip to content

Support Kubernetes-style ProductVariant frontmatter #83

@juliuskrah

Description

@juliuskrah

Summary

Add Kubernetes-style frontmatter support for ProductVariant resources so variants can model sellable SKUs, selected options, inventory controls, pricing, and media attachments under a parent product.

Scope

In scope:

  • Support ProductVariant documents with top-level apiVersion, kind, metadata, spec and status
  • Define ProductVariantSpec with:
    • title
    • sku
    • productRef
    • manageInventory
    • inventory
    • pricing
    • selectedOptions
    • media
  • Support markdown body description content for variant-specific notes/copy
  • Validate resource identity as kind: ProductVariant
  • Validate parent linkage semantics via productRef

Out of scope:

  • Variant pricing engine implementation
  • Inventory reservation workflows
  • Automatic generation of all option combinations

Implementation Notes

ProductVariant

  • apiVersion (string)
  • kind (string)
  • metadata (ObjectMeta)
  • spec (ProductVariantSpec)
  • status (ProductVariantStatus)

ProductVariantSpec

  • title (string) — Display title for the specific variant
  • sku (string) — Unique Stock Keeping Unit for the variant
  • productRef (ObjectReference) — Parent product reference
  • inventory (InventoryDefinition) — Variant inventory definition/details
  • pricing (PricingDefinition) — Variant pricing definition/details
  • selectedOptions ([]SelectedOptionDefinition)
    • selectedOptions.name (string)
    • selectedOptions.value (string)
  • media ([]MediaDefinition) — Similar to Product media definition

InventoryDefinition

  • managed (bool)
  • policy (string) - Allowed values are deny and backorder
  • stockLocationRefs ([]ObjectReference)

PricingDefinition

  • priceSet (PriceSet)
    • priceSet.name (string)
    • priceSet.prices ([]PriceTemplate)

PriceTemplate

  • name (string)
  • validFromTime (Time) Start date the price can be applied
  • validUntilTime (Time) End date the price is valid up to
  • quantity (QuantityDefinition)
    • quantity.min (int16)
    • quantity.max (int16)
  • currencyCode (string)
  • amount (Decimal)
  • strategy (StrategyDefinition)
    • strategy.type (string) The pricing strategy
  • priority (int8)
  • eligibility (EligibilityDefinition)
    • eligibility.operator (string) One of All or Any
    • eligibility.constraints ([]PriceRuleConstraint)
      • eligibility.constraints.name (string)
      • eligibility.constraints.expression (string)
        This is a CEL expression to evaluate the rule. e.g. region.code == 'EU', this price is applied if the cart context is in Europe. Multiple expressions are evaluated as OR.

ProductVariantStatus

  • conditions ([]Condition)
  • lastAppliedRevision (string)
  • observedGeneration (int64)
  • resolved (ResolvedProductVariantDefinition)
    • product.name (string)
    • product.uid (string)
    • product.resourceVersion (int64)
    • priceSet.hash (string)
    • priceSet.compiledAt (Time)
    • priceSet.name (string)
    • priceSet.priceCount (int64)
    • priceSet.currencies ([]string)
    • priceSet.strategies ([]string)
    • inventory.availableQuantity (int64)
    • inventory.managed (bool)
    • media[].name (string)
    • media[].url (string)
    • media[].contentType (string)

Validation constraints

  • sku must be unique across variants
  • productRef.name must reference an existing product
  • selectedOptions should align with options defined on parent product

Example Document

---
apiVersion: catalog.gitstore.dev/v1beta1
kind: ProductVariant
metadata:
  # name: Usually generated as a slug from the .spec.title
  namespace: ensi-store
  labels:
    gitstore.dev/brand: Apple
    gitstore.dev/vendor: Apple
    processor: M4 Max
    ram: 64GB
    storage: 1TB SSD
spec:
  title: "Macbook Pro 64GB 1TB SSD M4 (Silver / 17)"
  sku: MBP-M4MAX-SILVER-17-64-1TB
  productRef: 
    name: macbook-pro-64gb-1tb-ssd-m4
    apiVersion: catalog.gitstore.dev/v1beta1
    kind: Product
  inventory: 
    managed: true
    policy: deny
    stockLocationRefs: 
    - name: berlin-warehouse
      apiVersion: inventory.gitstore.dev/v1beta1
      kind: StockLocation
  pricing: 
    priceSet: 
      name: ps_mbp_m4max_silver_17_64_1tb
      prices:
      - name: base-eur
        currencyCode: EUR
        amount: "2499.00"
        strategy: 
          type: fixedUnitPrice
        priority: 0
      - name: eu-business
        currencyCode: EUR
        amount: "2399.00"
        strategy:
          type: fixedUnitPrice
        priority: 50
        eligibility:
          operator: All
          constraints:
          - expression: "region.code == 'EU'"
          - expression: "customer.group == 'business'"
      - name: eu-business-bulk-10
        currencyCode: EUR
        amount: "2299.00"
        strategy:
          type: fixedUnitPrice
        quantity:
          min: 10
        priority: 100
        eligibility:
          operator: All
          constraints:
          - name: european-union
            expression: "region.code == 'EU'"
          - name: business-customers
            expression: "customer.group in ['business']"
      - name: vip-summer-campaign
        currencyCode: EUR
        amount: "2199.00"
        strategy:
          type: fixedUnitPrice
        validFromTime: "2026-06-01T00:00:00Z"
        validUntilTime: "2026-06-30T23:59:59Z"
        priority: 200
        eligibility:
          operator: All
          constraints: 
          - expression: "customer.group == 'vip'"
          - expression: "params.salesChannel == 'web'"
  selectedOptions:
  - name: color
    value: silver
  - name: size
    value: "17"
  - name: ram
    value: 64GB
  - name: storage
    value: 1TB SSD
  media:
  - fileRef:
      name: macbook-hero
      optional: true
status:
  observedGeneration: 4
  conditions:
  - type: Published
    status: "True"
    observedGeneration: 4
    reason: ReleaseTagApplied
    message: Published at v1.0
  - type: AdmissionAccepted
    status: "True"
    observedGeneration: 4
    reason: PolicyPassed
  - type: ProductResolved
    status: "True"
    observedGeneration: 4
    reason: ProductFound
    message: Product reference resolved.
  - type: OptionsAccepted
    status: "True"
    observedGeneration: 4
    reason: OptionsMatchProduct
    message: Selected options are valid for parent product.
  - type: PricingAccepted
    status: "True"
    observedGeneration: 4
    reason: PriceSetValid
    message: 4 prices compiled.
  - type: Ready
    status: "True"
    observedGeneration: 4
    reason: Reconciled
    message: Variant is ready for checkout pricing.
  resolved:
    product:
      name: macbook-pro
      uid: 0a1a10e2-15f4-46b2-9e80-1c01e99dd3fb
      resourceVersion: "88"
    priceSet:
      name: ps_mbp_m4max_silver_17_64_1tb
      compiledAt: "2026-05-24T10:00:00Z"
      hash: sha256:...
      priceCount: 4
      currencies: ["EUR"]
      strategies: ["fixedUnitPrice"]
    media:
    - name: macbook-hero
      url: s3://catalog/macbook-hero.jpeg
      contentType: image/jpeg
    inventory:
      managed: true
      availableQuantity: 18
  lastAppliedRevision: main@sha1:a1b2c3d
---
Variant-specific description...

Acceptance Criteria

  • ProductVariant documents are supported with apiVersion: catalog.gitstore.dev/v1beta1 and kind: ProductVariant
  • ProductVariantSpec supports title, sku, productRef, manageInventory, inventory, pricing, selectedOptions, and media
  • Variant markdown body is supported as descriptive content
  • Validation enforces unique sku and valid parent productRef
  • Validation enforces option compatibility between selectedOptions and parent product options
  • Invalid variant documents are rejected when required fields are missing or kind is incorrect
  • Docs or examples for ProductVariant are updated as part of this task

Implementation Plan

  1. #208 — Contract and scope baseline
  2. #209 — Validation and parsing semantics (blocked by #208)
  3. #210 — Secondary domain constraints (blocked by #208)
  4. #211 — Integration tests and docs (blocked by #209 and #210)

Tracking

Metadata

Metadata

Assignees

No one assigned

    Type

    No fields configured for Task.

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions