← back to All Designerwallcoverings
docs/live-stock-creds-in-place.md
108 lines
# Live-stock for login-gated vendors — creds referenced IN PLACE (never copied)
**Status:** Thibaut wired as the ONE proof (2026-07-07). Expansion to the other vault vendors is
Steve-gated (see "Expansion plan" below).
## The problem
The live-stock coverage generator (`scripts/build-coverage.js`) enables a login-gated vendor only
when it finds trade creds in `dw_unified.vendor_registry.trade_password`. Several high-value
login-gated lines (Thibaut, Schumacher, Fabricut, Koroseal, Timorous Beasties, …) have **no creds in
`vendor_registry`** — their trade credentials live ONLY in the on-host credential vault at
`/root/DW-Agents/dw-price-stock/.env` (Parker's `.env`). So the grid honestly dims them
"no trade credentials on file," even though we *can* log in.
We want "Check Live" to work for those vendors **without** copying a single trade cred into this
repo's `.env`, into `dw_unified`, or onto any public surface.
## What Parker (`dw-price-stock`) actually is — and is NOT
Investigated read-only on prod (2026-07-07). `dw-price-stock` (pm2 name `dw-price-stock`, "Parker —
DW Price & Stock Checker", port 7205) is a **cost/price aggregator**: its `server.js` routes sync
costs from Shopify (GraphQL `inventoryItem.unitCost`), import price sheets from Google Drive and
Gmail attachments, and report MAP compliance / margin alerts. It has **no vendor-portal login code
and no live-stock scraping** — "trade portal scraping" appears only as recommendation *text*. It was
**not even running** at investigation time (7205 → `000`).
Consequence: the task's preferred design — "call Parker's existing stock lookup on-host" — **does
not exist**. Parker is not a stock service; it is simply the **home of the shared credential vault**
(`.env` holds `<VENDOR>_LOGIN_URL/_USERNAME/_PASSWORD` triples for ~30 vendors plus FTP/Shopify/
proxy creds). So we take the documented fallback: **an on-host adapter that references the vault by
path at runtime.**
## The design (creds stay entirely in the vault)
1. **`scripts/adapters/creds.js`** — a resolver that, for a given vendor ENV prefix (e.g. `THIBAUT`),
reads `<PREFIX>_USERNAME/_PASSWORD/_LOGIN_URL` from the vault file **by path**
(`process.env.DW_PRICE_STOCK_ENV`, default `/root/DW-Agents/dw-price-stock/.env`), parsed with
`dotenv.parse` **in memory only**. Values are held only for the duration of one login and are
**never** written back, logged, echoed, or copied into any `.env`/DB. Falls back to
`vendor_registry` (the existing wallquest/romo pattern) when the vault has no entry.
`DW_PRICE_STOCK_ENV` is a **path pointer, not a secret** — safe to place in this repo's `.env`.
2. **Per-vendor login adapter** (`scripts/adapters/<vendor>.js`) — same shape as the existing
`wallquest`/`romo` adapters: opens a Browserbase session, logs in using creds from `creds.js`,
resolves the SKU's PDP via `resolve.js`, reads availability, and returns a **PUBLIC-SAFE** result
(availability + our-retail only; trade/net price is `null` and never emitted).
3. **`build-coverage.js`** gains a small `VAULT_PORTAL_ADAPTERS` map (vendor → wired adapter). A
vendor in that map with a dedicated resolvable catalog is classified `PORTAL_CREDS`,
`live_capable:true`, still subject to the resolvability gate (`resolvable ≥ 5 && ≥ 20%`). The map
is placed **before** the public-Shopify branch because a vault-portal vendor's stock is
trade-gated — not exposed on the anonymous storefront.
Nothing about the paid live path, the daily `$` kill-switch, per-IP/burst caps, or the public-safe
contract changes — this only teaches coverage that "an adapter + vault creds exist."
## Why this keeps creds in place
- The vault file is read by **path**, parsed into process memory, used for one login, discarded.
- No trade cred is ever written to `all-designerwallcoverings/.env` or `dw_unified`. Only a **path
pointer** (`DW_PRICE_STOCK_ENV`) lives in this repo's `.env`.
- Live output is availability (+ our-retail) only — never cost/net/wholesale.
## Proof vendor — Thibaut
Chosen because it has vault creds, **passes the resolvability gate by the widest margin**, is the
highest-value of the eight, and its portal login is already proven workable across the fleet
(lowest single-session anti-bot risk).
Resolvability (prod dw_unified, 2026-07-07):
| Vendor | active | resolvable | pct | gate |
|-------------------|-------:|-----------:|----:|------|
| **Thibaut** | 2384 | 1619 | 68% | PASS |
| Schumacher | 2679 | 1057 | 39% | PASS |
| Fabricut | 3 | 1 | 33% | FAIL (tiny) |
| Koroseal | 2351 | 9 | 0% | FAIL (SKU↔URL misalign) |
| Designtex | 417 | 0 | 0% | FAIL (schema-public, misalign) |
| Contrado | 15 | 0 | 0% | FAIL (schema-public, misalign) |
| Timorous Beasties | 0 | 0 | 0% | FAIL (no active grid rows) |
| Folia | — | — | — | not a grid vendor |
Thibaut confirmed a genuine **live-stock** vendor (not price-only): the anonymous PDP is a Shopify
storefront exposing `availableForSale` / `stocked` / `backorder`, and states outright that
"stock status … [is] available to registered trade accounts" — i.e. live inventory is behind the
trade login. Login page: `https://www.thibautdesign.com/account/login` (`#email` + `#password`).
## Per-vendor live-stock feasibility for the eight
- **Live-stock capable + creds in vault:** Thibaut (PASS, wired), Schumacher (PASS, needs adapter).
- **Creds in vault but not gate-ready:** Fabricut (catalog too small), Koroseal, Designtex, Contrado
(display SKUs don't align with catalog `product_url`s — need a SKU remap before any adapter helps),
Timorous Beasties (no active grid rows under that name).
- **Folia** — not sold under a "Folia" display vendor in the grid; nothing to enable.
## Expansion plan (Steve-gated — do NOT wire until approved)
1. **Schumacher** — write `scripts/adapters/schumacher.js` (creds prefix `SCHUMACHER`,
`schumacher.com/catalog/products/<mfr_sku>`), add `schumacher` to `VAULT_PORTAL_ADAPTERS`. Gate
already PASSES (39%). Effort: ~1–2h + one careful proof session.
2. **Koroseal / Designtex / Contrado / Fabricut** — the blocker is **SKU↔product_url alignment**, not
creds. Effort is a per-vendor catalog remap (rebuild each `_catalog.product_url` keyed to the grid
SKUs), then Designtex/Contrado can even use the existing **login-free** `schema-public` adapter;
Koroseal/Fabricut would need a login adapter. Effort: ~2–4h per vendor (mostly catalog work).
3. **Timorous Beasties / Folia** — no grid rows to serve; skip until they exist in the catalog.
Each new portal = ONE careful proof session (anti-bot / trade-account-lock risk), never a mass login.