# Web100: inspectable cart-variant proof

This is an **intentionally constructed technical demonstration** of a stale-selection bug and a narrow patch. It is not a customer case study, a production WooCommerce or Shopify plugin, a revenue result, or evidence that any particular store has this defect.

## What it demonstrates

The sample overshirt has six variants: Black or White, each in S, M, or L. Each variant has an explicit SKU. Both carts start at Black / S (`DEMO-BLK-S`).

The original handler captures that first variant when the store initializes. Selecting White / L updates the visible selection but leaves the handler using `DEMO-BLK-S`. The patched handler reads the current selection when the add action occurs, so White / L adds `DEMO-WHT-L`.

Existing cart entries are snapshots. Changing the selection later must not change earlier entries. Invalid selections are rejected before state changes. Each mode has a separate in-memory cart.

## Files

- `index.html`: standalone demonstration page.
- `styles.css`: responsive black-and-white interface; styles are scoped to `.proof-page` except the standalone body's margin reset.
- `demo.mjs`: browser controls, visible state, live checks, JSON report download.
- `cart.mjs`: complete cart model with the deliberately broken and patched handlers.
- `regression.mjs`: seven explicit contract scenarios, runnable in the browser or Node.
- `cart.test.mjs`: dependency-free Node tests covering the patch, the original defect, all six SKUs, invalid state, isolation, and reset behavior.

## Run locally

Serve the parent folder with an existing static server. For example, from `web100-stage1`:

```sh
python3 -m http.server 8000 --bind 127.0.0.1
```

Open `http://127.0.0.1:8000/proof/`. Use HTTP rather than double-clicking `index.html`, because browser module imports can be blocked under `file:` URLs. The server must serve `.mjs` files with a JavaScript-compatible MIME type. No installation, package manager, API key, paid dependency, or build step is required.

## Run the tests

From the `proof` folder, using an existing Node installation with its built-in test runner:

```sh
node --test cart.test.mjs
```

Print the same seven-case report that powers the browser table:

```sh
node --input-type=module -e 'import { runRegression } from "./regression.mjs"; console.log(JSON.stringify(runRegression(), null, 2));'
```

The original handler is expected to pass only the unchanged initial-selection case and fail the other six contract cases. The patched handler must pass all seven. The Node suite passes when it correctly detects the deliberate defect and confirms the patched behavior; its success does not mean the broken handler passed the contract.

### Validation recorded on 4 November 2026

- Node `v24.22.1`: all 15 tests passed, with zero failures or skipped tests.
- `node --check` passed for `cart.mjs`, `regression.mjs`, and `demo.mjs`.
- Direct execution of the shared contract report returned original 1/7 and patched 7/7.
- Chrome verification on the Cloudflare preview: the example displayed the original mismatch and patched match; the browser report returned original 1/7 and patched 7/7 at 2026-11-04T09:24:26.044Z. No console errors or warnings were recorded during those checks.
- Main and proof pages were checked at a 390 px viewport override (375 px layout area excluding the scrollbar), with no horizontal document overflow.
- The main-page composer produced the expected recipient, subject and brief text using its Copy email text action. No message was sent by the page.
- Download results produced `web100-variant-regression.json` (7,848 bytes), verified from the actual browser download. The JSON contains the matching 2026-11-04T09:43:05.577Z execution timestamp and original 1/7, patched 7/7 results, including the expected and actual arrays for all seven cases.

## Acceptance checks

1. On a fresh page, both selections show `DEMO-BLK-S`, both carts are empty, and the browser report says it has not run.
2. Activate **Run example in both modes**. Both controls change to White / L. The original result shows selected `DEMO-WHT-L`, added `DEMO-BLK-S`, and an explicit mismatch. The patched result shows `DEMO-WHT-L` for both values and an explicit match.
3. In the patched panel, choose Black / M and add it. Inspect the cart. The earlier White / L line remains unchanged, followed by Black / M.
4. Change a selection without adding. **Selected now** changes; **Selected at click** continues to describe the previous add action.
5. Activate **Run seven checks**. Seven rows show explicit expected SKUs, actual SKUs, and PASS/FAIL for both handlers. The report must show original 1/7, patched 7/7. Running it must not change the demonstration carts.
6. Activate **Download results**. The JSON contains the execution timestamp, scope disclosure, steps, expected and actual arrays, and booleans for the actual run.
7. Activate **Reset both**. Both carts empty and both selections return to Black / S. Refreshing the page also clears all state.
8. Navigate every control by keyboard. Labels identify the panel and fields; result changes are announced as status messages. At a narrow viewport, the panels stack and the results table scrolls within its own region.

## Patch and limits

The essential patch is reading `selectedVariant` at action time instead of using `capturedVariant`. The displayed snippets are explanatory excerpts; `cart.mjs` is the authoritative complete source.

This local model does not implement inventory, unavailable combinations, asynchronous product requests, server validation, checkout, payment processing, user authentication, taxes, discounts, persistent carts, network retries, or a real commerce platform. Its checks establish only the documented local contract. Production work requires reproducing the actual client's defect, identifying its true cause, agreeing scope, and testing the actual platform in staging before an authorized deployment.

All execution is local to the browser or Node process. The page has no external assets, analytics, customer data, backend, or external requests. Refreshing clears all in-memory data.

## Integration

Copy the complete `proof/` folder beneath a static web root and serve `/proof/`. Relative module and stylesheet URLs remain valid. The back link resolves to the containing site's root. If embedding the markup in another page, retain the `.proof-page` wrapper and remove the standalone `body { margin: 0; }` rule if the host page should control that margin.
