PRVKE 31L V4

How this map was built

Methodology

Capture rules, provenance model, conflict handling, and the limits of public observability. The full method is also committed as METHODOLOGY.md.

Provenance labels
Every ledger row carries exactly one label. Inferred fields are never described as merchant-authored.
LabelApplied to
MERCHANT_AUTHOREDRendered PDP copy, theme-emitted JSON-LD, merchant docs, support/policy pages.
PUBLIC_SHOPIFYPlatform-served public surfaces: product JSON endpoints, embedded theme/app state, sitemap/robots.
STOREFRONT_CATALOGMerchant-scoped UCP catalog responses.
GLOBAL_BASEBase UCP fields in Global Catalog responses.
GLOBAL_INFERREDFields Shopify documents as generated or enriched (never merchant-authored).
SELLER_OFFERGlobal variant seller objects, checkout URLs and offer availability.
SEARCH_ENGINE_OBSERVEDSearch engine result title/snippet/URL; never product truth.
Overview

# Methodology

Scope

One store (`www.wandrd.com`), one product family (PRVKE V4), one target

(PRVKE 31L, Black / Bag Only where a variant is required). Sibling colourways and

sizes are captured only to prove identity scope and fragmentation; they are never

merged into target facts. No reseller is treated as merchant truth.

Frozen target identity

The apex host redirects to `www` (`x-redirect-reason: canonical_host_redirection`);

the capture records the full redirect chain.

Capture rules
  • Plain HTTP only (Python stdlib); **no page JavaScript is executed**.
  • Requests are paced at ≥0.9 s; 429/503 and "Verifying your connection" challenge

pages are retried with exponential backoff. Challenge pages that remain are kept

as observed failures, never parsed as product data.

  • Headers are normalised to lower-case when stored in `raw/http-meta/`.
  • Raw captures are immutable: identical re-captures are recorded in the manifest

without rewriting; non-identical re-captures create numbered duplicates.

  • The task's shorthand `raw/pdp.html` is provided as a symlink to

`raw/pdp/pdp.html`; the structured layout in `raw/` is authoritative.

Source inventory
Provenance model

Shopify documentation is preserved under `raw/docs/` and is the basis for the inferred

classification. Inferred metadata is never described as merchant-authored.

Fact ledger

`normalized/facts.jsonl` carries one fact per line with:

`fact_id`, `subject`, `predicate`, `value`, `unit`, `conditions`, `source`,

`source_field`, `source_url`, `evidence`, `provenance`, `status`, `conflict_group`,

`retrieved_at`.

  • Subjects are scoped: `PRVKE 31L V4` (model), `… / Black / Bag Only` (variant),

`… / Photography Bundle (bundle)` (bundle).

  • `status` is `SUPPORTED`, `CONTRADICTED`, `CONFLICTED` or `UNKNOWN`; the ledger

generator only emits `SUPPORTED`/`CONFLICTED`, and conflicts are detected by

comparing semantically-normalised values inside a `conflict_group`

(`true`≡`InStock`, numeric strings, cube-model sets). Conflicts are never resolved

silently: every member keeps its own value, scope and source.

  • Facts receive a `source_url` and/or `source_field`; `evidence` quotes the field or

value actually observed.

Analysis products
  • `analysis/source_matrix.{csv,md}` — 42 semantic fields × 9 source columns with

`EXACT`/`PRESENT`/`PARTIAL`/`TRANSFORMED`/`ABSENT`/`CONFLICT`/`NOT_APPLICABLE` and

back-pointers to ledger fact IDs.

  • `analysis/diff.md` — nine-section field-level diff (the primary artifact).
  • `analysis/conflicts.{json,md}` — conflict groups with scopes and sources.
  • `analysis/provenance.md` — label counts and application rules.
  • `analysis/field_coverage.json` — per-field provenance coverage and merchant gaps.
  • `data/verification.md` — 14 automated PASS/FAIL checks.
Rate-limit handling (Shopify and GitHub)

**Shopify / Cloudflare.** WANDRD is behind Cloudflare with Shopify's bot mitigation.

Observed during development: bursty sequential requests (and especially the large

`/products.json?limit=250` listing) can answer `429`/`503` with a

`Verifying your connection…` HTML body and no `Retry-After`. The capture layer therefore:

  • sends a current desktop-browser User-Agent (the research token was removed),
  • persists a per-host cookie jar in `data/cookies/` so the session established by the

PDP visit is reused (cookies are git-ignored, never committed),

  • enforces a per-host minimum interval (default 1.2 s) that *tightens* on any challenge

(×1.8, capped at 30 s) and relaxes after success,

  • detects challenges by status (403/429/503), `cf-mitigated: challenge`, and body marks,
  • retries up to 5 times with jittered exponential backoff, honouring `Retry-After`

(capped at 180 s),

  • records every attempt in `data/capture_manifest.jsonl` and the probe tables,
  • keeps any persistent challenge page as a preserved source failure and never parses it

as product data; `/products.json` additionally has a documented fallback to

`/collections/all/products.json` if the listing endpoint stays blocked.

**GitHub.** Authenticated REST quota observed: 5,000 requests/hour, 30 search

requests/minute, plus secondary burst limits (403 + `retry-after`). `scripts/gh.py`

paces calls, reads `x-ratelimit-remaining`/`x-ratelimit-reset` and pauses below a

25-request watermark, backs off on 403/429/5xx, and creates the repository only after a

`GET` confirms it is absent. `scripts/push.sh` retries SSH pushes (not part of the REST

quota) with linear backoff.

Known limitations
  • Observations are point-in-time (2026-09-28, US market, USD); prices, availability

and inferred metadata can change without notice.

  • The merchant is on Shopify where `/products.json` is capped at 250 records per page;

only page 1 was inspected (sufficient to confirm the target's presence and sibling

records) — the store was not fully enumerated by design.

  • Global Catalog search captures are resolver evidence; the persistent evidence set is

the exact `lookup_catalog`/`get_product` captures.

  • The UCP `search_catalog` responses are point-in-time and are not intended to be

reused as a permanent search cache.

  • Some embedded state (Okendo reviews, web pixels) is merchant-app data served on the

public page; it is classified `PUBLIC_SHOPIFY` as an observed platform surface, not

as a documented product API.

  • "Customers Also Purchased" recommendations are rendered from the theme's own state;

their ordering/pricing can vary by traffic and time.

  • Image comparison is metadata-only; images are not downloaded or rehosted.
Not publicly observable
Anything below is outside the observable set; the dashboard shows “Not publicly observable” instead of guessing.
  • Private Admin metafields/metaobjects not emitted by the theme; Shopify Catalog mapping
  • configuration, saved catalogs and promoted placements; exact multi-location inventory
  • (only the storefront-visible quantity leaks), reserved/committed/incoming stock; cost,
  • margin, supplier; orders and customers; discount configuration; internal ranking and
  • personalisation features; and any inference input Shopify does not expose. Completeness
  • is claimed only for what the captured public surfaces returned.
Verification
18 PASS / 0 FAIL of 18 automated checks, run against the committed captures.
all passing

PASSTarget identity is consistent

product id {7195497005136}; canonical {'https://www.wandrd.com/products/prvke-31l-in-black'}; handle prvke-31l-in-black; Global UPID gid://shopify/p/14pdF53KLMZSW6Q3wodClH; Storefront product gid://shopify/Product/7195497005136.

PASS31L is not mixed with 21L/41L

capacity facts: [('capacity_normal', 31), ('capacity_expanded', 35)]; Global scope UPIDs: ['gid://shopify/p/5IWcrTkvmEhv4QAyaUWLDQ', 'gid://shopify/p/5bNJbuSc5mIMajMIQ7ZOIM'] (target UPID excluded); dimension table 31L: {'volume': '31L-35L', 'weight': '3.3lbs(1.49kg)', 'height': '18.5"(47CM)', 'width': '12"(30.5CM)', 'depth': '6.5"(16.5CM)'}.

PASSBundle facts remain bundle-scoped

bundle_includes subjects: ['PRVKE 31L V4 / Bag Only (bundle)', 'PRVKE 31L V4 / Photography Bundle (bundle)', 'PRVKE 31L V4 / Pro Photography Bundle (bundle)']; unscoped rows: [].

PASSVariant facts remain variant-scoped

SKU subjects: ['PRVKE 31L V4 / Black / Bag Only', 'PRVKE 31L V4 / Black / Photography Bundle', 'PRVKE 31L V4 / Black / Pro Photography Bundle']; unscoped rows: [].

PASSFirst-party WANDRD is distinguishable from resellers

target UPID seller domains: ['wandrd-development.myshopify.com']; reseller contrast: gid://shopify/p/5lONwZeLtEtApxQKHDlRR6 ['hotrodcameras.myshopify.com'].

PASSShopify-inferred fields are labelled GLOBAL_INFERRED

11 inferred facts; mislabeled: none; documented inferred fields: ['description', 'metadata.attributes', 'metadata.tech_specs', 'metadata.top_features', 'metadata.unique_selling_points', 'options', 'variants[].condition'].

PASSEvery normalized fact points to raw evidence

facts=359; missing evidence/source: none; raw refs checked: 24; missing raw files: none.

PASSNo unsupported fact enters the ledger

bad provenance/status: none; null-value facts not explicitly absence-type: none.

PASSRaw captures are immutable

manifest entries=141; distinct raw files hashed=141; hash mismatches: none; actions={'created': 140, 'created-dup': 1}.

PASSAll timestamps are recorded

facts without retrieved_at: none; probes with captured_at: True; discovery captured_at: True.

PASSAll source failures are preserved

404 probes: [('products/accessory-straps.js', 404, 'raw/support/FAILED_products_accessory-straps_js.html'), ('products/camera-cube.js', 404, 'raw/support/FAILED_products_camera-cube_js.html'), ('pages/size-guide', 404, 'raw/support/FAILED_pages_size-guide.html'), ('pages/prvke-size-guide', 404, 'raw/support/FAILED_pages_prvke-size-guide.html')].

PASSNormalized views reference existing raw captures

raw paths referenced=51; missing=none.

PASSVariant merge is 31L/Black-scoped

variants=[(41232562225232, 'PK31-BK-4'), (41232562258000, 'PK31-BK-PB-4'), (41232562290768, 'PK31-BK-PPB-4')].

PASSDiscovery claims match captures

sitemap target present=True; agents.md UCP endpoint=[200]; well-known UCP=[200].

PASSNo rate-limit challenge page is used as data

challenge-marked files under required capture roots: none; valid product listing with target handle: True.

PASSGitHub rate-limit guard and retrying push are in place

scripts/gh.py present with watermark: True; scripts/push.sh present: True.

PASSCapture manifests record retry attempts

manifest entries=141; http entries=138; with attempts=116.

PASSRequired normalization schema keys are present

jsonld normalized_product_fields missing: none; embedded missing: none; global metadata.attributes check recorded: True.