# Reproduce the product-price currency case

These files accompany actual observations of one Finisterre product on 11 September 2026 UTC. They provide a diagnostic example, not current prices or a general-purpose scraper.

## Offline replay

Download replay.py and selected-records.zip into one directory. Python 3.10 or newer and its standard library are sufficient:

```sh
python3 replay.py selected-records.zip
```

Compare stdout with results.json. The replay makes no network requests and needs no proxy credentials. It independently recomputes decisions from selected identity, amount, currency and URL fields, and checks the complete cell plan, request references and accounting. This dataset has 16 page observations, 48 GETs, 12 currency conflicts and four internally agreeing records. Conflicts are legitimate measured results; corrupted, missing or inconsistent records exit 2. Passing within-record checks is not a checkout quote or sufficient evidence to compare different markets.

The archive has four root files: manifest.json, requests.jsonl, cells.jsonl and completion.json. Request rows omit only the original private_body and private_headers capture paths. Raw response bodies, unfiltered headers, cookies, exit addresses and credentials are private. Hashes preserve body identity for the original audit, but offline replay cannot authenticate a historical server response or inspect unpublished raw bodies. method.json includes exact code/archive hashes, request budgets, timing, browser restrictions and incomplete calibration.

## Test the published code

Download market_study.py, test_market_study.py, replay.py and test_replay.py into the same directory:

```sh
python3 -m unittest discover -s . -p 'test_*.py'
```

The 30 passing tests recorded in test-results.txt use synthetic fixtures only. The fixtures are not retailer observations. They exercise variant selection, currency and URL-context checks, redirects and failures, bounded gzip handling, private transport configuration, incomplete data and corrupted archive rejection.

## Optional authorized collection

The actual executed source is market_study.py and its public input is study.json. Replaying the archive is enough to inspect this finding. A fresh collection makes real network requests and may consume proxy allowance. Review the target's access rules and use only authorized routes. The recorded study sent only bounded public product GETs; it made no cart, checkout, account or localization submissions.

Create a private, mode-0600 JSON file outside source control with a contexts object: direct is null; us, gb and nl each contain server, username and password. server is an HTTP proxy URL without embedded credentials. The code passes authentication privately through curl stdin. Set IPVOLT_STUDY_SECRETS to that file's absolute path without printing its contents. Use curl 8.4 or newer; the collector relies on its download-size limit enforcement:

```sh
python3 market_study.py collect study.json NEW_PRIVATE_RUN --secrets-env IPVOLT_STUDY_SECRETS
python3 market_study.py summarize NEW_PRIVATE_RUN
```

Use a new private output directory. Its raw bodies and headers must not be published without an explicit privacy review. summarize reports collector fields; use the separate replay for independent selected-field validation. The executed collector is preserved unchanged, including its assumption of absent Ajax currency. The replay adds a check rejecting an explicit Ajax currency that contradicts the effective page context.

The HTML selectors, product/variant/SKU identities and GBP/USD/EUR decimal formats are specific to the observed product. The x100 API comparison is not a universal currency-unit rule. To adapt this to a property you operate, review the allowed host and product paths, replace the exact selectors and identity, and verify amount scale and currency evidence against actual responses. Do not treat a missing or ambiguous field as a usable price. An original-prefix JSON amount without its own currency must not borrow the redirected HTML's currency.

## Files

- replay.py: self-contained offline validator.
- selected-records.zip: complete selected main observations.
- results.json: actual replay output.
- method.json: main and auxiliary methodology, counts and limits.
- market_study.py: exact executed collection source.
- study.json: exact public collection configuration.
- test_market_study.py and test_replay.py: controlled offline tests.
- test-results.txt: recorded local test output.
- README.md: these instructions.
