Skip to content

Conformance: wholesale conditional-fetch has no cache_scope-isolation storyboard — global-token implementations pass the suite #5739

Description

@EvgenyAndroid

Summary

3.1 specifies wholesale_feed_version as scope-keyed:

"A returned wholesale_feed_version is scope-keyed via cache_scope. Callers cache (cache_scope, wholesale_feed_version) pairs alongside the (account, filters, discovery_mode, destinations, countries) tuple used."

The signals/products storyboards exercise the positive conditional-fetch round-trip (echo the token → unchanged: true) but never the scope-isolation negative: a token issued for one cache_scope MUST NOT yield unchanged: true on a request that resolves to a different cache_scope. The requirement is implied but untestable, so a seller that computes a single global feed token — ignoring cache_scope — passes the suite while silently serving stale unchanged: true across scopes.

Evidence (empirical)

Implementing 3.1 wholesale mirroring on a reference signals agent, a deliberately simple global-token build:

  • emits wholesale_feed_version on every wholesale response and honors if_wholesale_feed_versionunchanged: true (happy path ✓);
  • passes the 9.2.0 storyboard grader 7 / 0;
  • yet live-returns unchanged: true for an account-scoped request presenting a cache_scope: public token (the token recomputes identically because scope isn't part of the input).

The suite currently certifies a build that violates the scope-keying contract.

Proposed

1. Storyboard wholesale_conditional_fetch_isolation (signals; mirror for products):

  1. get_signals(discovery_mode: "wholesale") → assert wholesale_feed_version and cache_scope present.
  2. Replay with if_wholesale_feed_version: <token> → assert unchanged: true, signals omitted, cache_scope present.
  3. Scope isolation: present the public-scope token on a request that resolves to cache_scope: account (e.g. account-overlay pricing) → assert NOT unchanged: true. The normative claim is scope-based, not identity-based: the token was issued for cache_scope: public; re-presenting it on a request that yields cache_scope: account MUST NOT short-circuit.
  4. (Optional) Mutate the catalog, replay the stale token → assert full payload (token rolled).

2. Normative tightening on get-signals-response.json / get-products-response.json (+ the if_wholesale_feed_version request fields):

A seller MUST NOT return unchanged: true when the cache_scope the request resolves to differs from the cache_scope the presented token was issued for.

Design notes (already checked)

  • The MUST-NOT lives on the conditional-fetch handler — a scope comparison between the presented token's issuing scope and the scope this request resolves to. It is unrelated to access control; the failure mode is staleness, not authorization.
  • Step 4 seeding: force_get_signals_arm explicitly MUST NOT be consumed on wholesale reads, so it can't drive the mutation step. The storyboard should mutate via a seed_* fixture (or a direct test-controller catalog mutation) instead.

Same shape as the prior merged conformance fixes (#5429 / #5444). Happy to open the storyboard PR if the scenario shape lands.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions