id: wholesale_feed_signals
version: "1.0.0"
title: "Wholesale signals feed versioning"
category: marketplace_catalog
summary: "Validates get_signals wholesale feed versioning: bootstrap responses carry wholesale_feed_version/cache_scope and matching if_wholesale_feed_version probes return unchanged without signal rows."
track: signals
introduced_in: "3.1"

requires_capability:
  path: wholesale_feed_versioning.supported
  equals: true

required_tools:
  - get_signals

narrative: |
  Signals agents that advertise wholesale feed versioning and expose
  `get_signals` must support the beta.4 conditional-fetch shape for wholesale
  signal mirrors:
  `discovery_mode: wholesale`, `wholesale_feed_version`,
  `if_wholesale_feed_version`, `cache_scope`, and `unchanged: true`. This
  storyboard does not require the optional separate `pricing_version` layer.

agent:
  interaction_model: signals_agent
  capabilities:
    - wholesale_feed_versioning
  examples:
    - "Signals agents with a wholesale signals feed"

caller:
  role: buyer_agent
  example: "Compliance test harness"

prerequisites:
  description: |
    The agent advertises `wholesale_feed_versioning.supported: true` and exposes
    `get_signals`.
  test_kit: "test-kits/acme-outdoor.yaml"
  controller_seeding: false

phases:
  - id: conditional_fetch
    title: "Bootstrap and probe signals feed"
    steps:
      - id: bootstrap_signals
        title: "Bootstrap signals feed"
        task: get_signals
        schema_ref: "signals/get-signals-request.json"
        response_schema_ref: "signals/get-signals-response.json"
        doc_ref: "/signals/tasks/get_signals"
        stateful: true
        context_outputs:
          - name: signal_wholesale_feed_version
            path: "wholesale_feed_version"
        expected: |
          Return signal rows with wholesale feed metadata.
        sample_request:
          discovery_mode: "wholesale"
          account:
            brand:
              domain: "acmeoutdoor.example"
            operator: "pinnacle-agency.example"
          context:
            correlation_id: "wholesale_feed_signals--bootstrap"
        validations:
          - check: response_schema
            description: "Response matches get-signals-response.json schema"
          - check: field_present
            path: "signals[0].signal_agent_segment_id"
            description: "Bootstrap returns signal rows"
          - check: field_present
            path: "wholesale_feed_version"
            description: "Bootstrap returns an opaque feed token"
          - check: field_value
            path: "cache_scope"
            value: "public"
            description: "Ordinary account reads may use the public cache layer"
          - check: field_value
            path: "context.correlation_id"
            value: "wholesale_feed_signals--bootstrap"
            description: "Context correlation_id returned unchanged"

      - id: unchanged_probe
        title: "Probe unchanged signals feed"
        task: get_signals
        schema_ref: "signals/get-signals-request.json"
        response_schema_ref: "signals/get-signals-response.json"
        doc_ref: "/signals/tasks/get_signals"
        stateful: true
        expected: |
          Return unchanged without resending signal rows when the feed token matches.
        sample_request:
          discovery_mode: "wholesale"
          account:
            brand:
              domain: "acmeoutdoor.example"
            operator: "pinnacle-agency.example"
          if_wholesale_feed_version: "$context.signal_wholesale_feed_version"
          context:
            correlation_id: "wholesale_feed_signals--unchanged"
        validations:
          - check: response_schema
            description: "Response matches get-signals-response.json schema"
          - check: field_value
            path: "unchanged"
            value: true
            description: "Matching token produces the cheap unchanged response"
          - check: field_equals_context
            path: "wholesale_feed_version"
            context_key: "signal_wholesale_feed_version"
            description: "Unchanged response echoes the current signal feed token"
          - check: field_value
            path: "cache_scope"
            value: "public"
            description: "Unchanged response carries cache scope"
          - check: field_absent
            path: "signals[0].signal_agent_segment_id"
            description: "Unchanged response omits signal rows"
          - check: field_value
            path: "context.correlation_id"
            value: "wholesale_feed_signals--unchanged"
            description: "Context correlation_id returned unchanged"

      - id: standalone_pricing_token_rejected
        title: "Reject standalone signal pricing token"
        task: get_signals
        schema_ref: "signals/get-signals-request.json"
        response_schema_ref: "signals/get-signals-response.json"
        doc_ref: "/signals/tasks/get_signals"
        expect_error: true
        negative_path: payload_well_formed
        sample_request_skip_schema: true
        stateful: true
        expected: |
          Reject if_pricing_version when if_wholesale_feed_version is absent.
        sample_request:
          discovery_mode: "wholesale"
          account:
            brand:
              domain: "acmeoutdoor.example"
            operator: "pinnacle-agency.example"
          if_pricing_version: "stale-pricing-token"
          context:
            correlation_id: "wholesale_feed_signals--standalone_pricing"
        validations:
          - check: error_code
            value: "INVALID_REQUEST"
            description: "Standalone if_pricing_version is rejected"
