# USA Clean — Product & UX decision log

Decisions made with the client that shape the prototype. This is the **decision of record** for each call: it captures *what* was decided, *why*, *when we'll revisit it*, and — critically — *which prototype/doc surfaces changed to reflect it* (the **Reflected in** field). It's the product/UX companion to the design-system [`decisions.md`](../design-system/decisions.md), which covers token & component architecture.

The styled, client-facing version is [`decisions.html`](decisions.html) (the client views it on `demo.thecxlabs.com`). This `.md` is the canonical source.

## How to use this log

When a decision is made with the client:

1. **Add an entry here.** Give it the next `PD-NNN` id (Product Decision). Fill every field — especially **Reflected in**, which lists the files changed so a reviewer can trace the decision back to the prototype.
2. **Mark the prototype.** At each point of change in the code, drop a comment marker so the *why* lives next to the *what* and stays greppable (`grep -rn "DECISION PD-" .`):
   - HTML — `<!-- DECISION PD-001 (2026-05) · one-line summary. See docs/decisions.md (PD-001). -->`
   - JS / CSS — `/* DECISION PD-001 — one-line summary. See docs/decisions.md (PD-001). */`
3. **Link it from the relevant brief.** Add a one-line "Decision of record: PD-NNN" pointer near that brief's recommendation, so the brief (the reasoning) and the log (the ruling) stay connected.

### Status legend

| Icon | Status | Meaning |
|---|---|---|
| ✅ | Decided | Settled and reflected in the prototype. |
| ⏸️ | Data-gated | Deferred on purpose; reopens when a named signal arrives (see **Revisit when**). |
| 🟡 | Open | Still under discussion; not yet reflected. |
| ↩️ | Reversed | Superseded by a later decision (link to it). |

### Entry template

```markdown
### ✅ PD-NNN — <short title>  ·  <date>, <forum, e.g. client review>
**Decision.** What was decided, in one or two sentences.
**Why.** The reasoning and what it traded off.
**Revisit when.** (data-gated only) The signal that reopens it.
**Reflected in.** The files/surfaces changed to implement it.
**Source.** Link to the brief or research that informed it.
```

---

## Decisions

### ✅ PD-001 — PDP bundle: ship Concept A, defer Concept C (data-gated)  ·  2026-05, client review
**Decision.** The PDP ships the "Frequently Bought Together" bundle (**Concept A**) as the production pattern. The "Build Your Bundle" slot configurator (**Concept C**) is deferred to a data-gated Phase 2. Concept B (curated "Service-Day Kit") was retired at the same review.

**Why.** Concept A works on every PDP with no per-product curation, is the most familiar pattern, and is the lowest implementation risk — so we can launch broadly and gather data first. Concept C's higher-AOV upside (4-pack / kit-version upsells in the alternates picker) is real, but it needs evidence before we invest in the per-product compatibility/alternates taxonomy it depends on.

**Revisit when** — on PDPs that carry the bundle vs. control, we see: (1) measurable **AOV lift**; (2) observable **swap intent** — users clicking alternates or removing/replacing bundle items; and (3) a meaningful share of users completing the FBT add-all flow. Those three signals graduate Concept C from Phase 2 to a build. Full triggers in the brief.

**Reflected in.**
- `v2/pdp.html` — Concept C removed (markup, scoped `.bundle-c*` CSS, and `bundleC*` JS); Concept A reframed as the production bundle (the "Bundle concept variations" comparison header and the "Concept A / Concept C" labels were dropped). Marked in-page with `<!-- DECISION PD-001 … -->`.
- `docs/bundle-concepts.md` / `.html` — kept as the reasoning of record; Concept C stays fully documented there as the Phase-2 path, with a "Decision of record: PD-001" pointer added.
- *Intentionally left as-is:* the original `pdp.html` (and its `legacy/` copy) comparison gallery still shows all three concepts — it's preserved as the exploration artifact. This decision applies to the active v2 prototype.

**Source.** [bundle-concepts.md](bundle-concepts.md) (§ Recommendation).

### ✅ PD-002 — Prototype review notes: capture in-page, store as GitHub Issues  ·  2026-06, internal tooling decision
**Decision.** The prototype carries a gated, ship-but-hidden **live review notes** layer (turn on with `?notes=1` or `Cmd/Ctrl+Shift+N`). A reviewer clicks any element to attach a **comment / decision / update / bug** with a **status** (open / approved / ready-for-dev). Notes persist in `localStorage` and are filed as **GitHub Issues** (labeled `review` + `type:*` + `status:*`): Phase 1 via GitHub's pre-filled new-issue form (works for repo collaborators), Phase 2 via an optional serverless proxy so non-GitHub clients can file directly. The `triage-notes` skill turns `type:decision` issues into `PD-NNN` entries in this log and the rest into `docs/backlog.md`.

**Why.** Reviews happen in-browser, but feedback was living in chat/memory and being hand-carried into this log. Anchoring a note to the *actual element in the actual layout* removes "which button?" ambiguity and gives every note a durable home that already feeds the two surfaces we maintain (this log + a backlog). GitHub Issues was chosen over a hosted store (nothing to run/pay for alongside the static FTP host) and over localStorage-only (not shareable or referenceable across people/sessions). Gating keeps the tool invisible to casual visitors while letting us invite stakeholders to annotate the real prototype.

**Reflected in.**
- `js/review-notes.js` + `css/review-notes.css` — the annotation layer, namespaced `.ucnote-*` so it never collides with Caster (`--caster-*`) or showcase chrome (`.ds-*`).
- `js/app.js` — a ~30-line gated loader that injects the layer on every page that loads `app.js` (zero per-page edits); off by default.
- `.claude/skills/triage-notes/SKILL.md` — converts `review` issues into this log + `docs/backlog.md`.
- `tools/ucnote-worker/` — Phase-2 proxy source (Cloudflare Worker); excluded from the FTP/SFTP deploys so no token/source reaches the host.
- GitHub label taxonomy (`review`, `type:*`, `status:*`, `triaged`) created on `longbrook-design/usaclean-research`.

**Source.** Internal tooling need (this build). Phase-2 setup in [`tools/ucnote-worker/README.md`](../tools/ucnote-worker/README.md).

### ⏸️ PD-003 — Fleet/equipment view out of the initial B2B  ·  2026-06, client review
**Decision.** The account dashboard's **Fleet snapshot** section, and the duplicate **"Fleet" quick-action** tile that links to it, are out of scope for the initial B2B launch. Both are kept in the source (commented out, not deleted) so they restore cleanly when the fleet/equipment phase is built.

**Why.** Fleet/equipment tracking is a later-phase National Account capability, not a day-one B2B need. Showing it on the initial dashboard over-promises functionality (machine service status, per-machine compatible-parts counts) that the initial release won't back with real data. Commenting out rather than deleting keeps the worked design available for the phase that owns it.

**Revisit when** — the National Account fleet/equipment phase enters scope (see the phase map in [`account-phases.html`](account-phases.html)). At that point uncomment the snapshot + quick-action and wire them to real equipment data.

**Reflected in.**
- `v2/account.html` — three fleet/equipment entry points are wrapped in HTML comments (each tagged `<!-- DECISION PD-003 … -->`) so they restore cleanly: the `<!-- Fleet snapshot -->` `<section class="card">`, the `.quick-action` "Fleet" tile, and the **Equipment** `.acct-tabs__item` tab. The `.fleet-*` CSS is left in place (harmless; restores with the markup). The storefront primary-nav "Equipment" item is a product category and stays.

**Source.** Client review, 2026-06-03. Phasing context in [`account-phases.html`](account-phases.html).

### ⏸️ PD-004 — Model-fit badge ("Fits &lt;model&gt;") is data-gated  ·  2026-06, client review
**Decision.** The cart line-item **"Fits Tennant T500e"** badge stays in the prototype as the intended end-state, but it's understood to be **aspirational**: it requires the part→model compatibility mapping, which doesn't exist yet. No UI change now; flagged so we don't read it as shippable.

**Why.** The fit badge is a strong trust signal for B2B buyers ("yes, this part fits your machine"), so we keep it in the design. But it can only be truthful once every SKU is mapped to the models it fits — a data/taxonomy effort that's a future implementation. Marking it here prevents it from being treated as build-ready before that mapping lands.

**Revisit when** — the part→model compatibility mapping is available (related to the search/model-mapping work). Then the badge can be driven by real data across the catalog.

**Reflected in.**
- `v2/cart.html` — `<!-- DECISION PD-004 … -->` marker beside the first `.cart-row__pill--fit` instance; the badge itself is unchanged on all cart rows.

**Source.** Client review, 2026-06-03.

### 🟡 PD-005 — Equipment PDP: Concept A vs Concept B  ·  reserved — decision pending
**Decision.** Pending — no client decision yet. This id is reserved for the equipment PDP direction: **Concept A** (spec-forward) vs **Concept B** (guided). The markers already sit in the prototype so the ruling can land without renumbering.

**Reflected in (so far).** `v2/equipment-*.html` carry `<!-- DECISION PD-005 … -->` markers; `docs/equipment-concepts.md` / `.html` and `reviews/equipment.html` point at this id.

**Source.** [equipment-concepts.md](equipment-concepts.md) (§ Recommendation).

### 🟡 PD-006 — Loyalty mechanic for the self-serve buyer  ·  reserved — decision pending
**Decision.** Pending — no client decision yet. Reserved for the loyalty-program ruling (freight-benefit tiers on native Customer Groups, per the brief's recommendation).

**Reflected in (so far).** `docs/loyalty-concepts.md` / `.html` and `reviews/loyalty.html` reference this id.

**Source.** [loyalty-concepts.md](loyalty-concepts.md) (§ Recommendation).

### ✅ PD-007 — Gated Q&A assistant on the demo (hosted Worker + paid API)  ·  2026-08, internal tooling decision
**Decision.** Add **UC Assist**, a passcode-gated Q&A assistant available from every page of the demo. It answers questions about the prototype, the Caster design system, and the research briefs, geared to a chosen audience (client / design / dev / product), citing the pages it drew from. Its knowledge domain is a corpus generated from this repo and nothing else; out-of-domain questions get a fixed refusal.

This is a **deliberate departure from [PD-002](#)'s stated principle**. PD-002 chose GitHub Issues for review notes precisely because there was "nothing to run/pay for alongside the static FTP host." An LLM endpoint cannot be static: it needs a server to hold the API key and a paid API to answer. We are accepting a hosted service and a metered cost for this feature, and only this feature.

**Why.** The prototype has grown past the point where a stakeholder can find things by clicking: 13 feature briefs, a 30-page design-system showcase, two decision logs, and roughly 100 pages of prototype. The material to answer almost any question already exists in the repo and is already published to the demo — what is missing is a way to ask. Everything the assistant says is grounded in that published material, with links back to it, so it shortens the path to a document rather than replacing one.

What makes the cost acceptable rather than open-ended:
- **Gated.** Origin allowlist plus a shared passcode the client enters once (the [ucnote-worker](../tools/ucnote-worker/README.md) pattern), so it is not open to the internet.
- **Capped.** A spend cap on a dedicated Anthropic workspace is the hard ceiling; per-IP rate limiting and a daily budget brake sit in front of it. Cost per request is logged.
- **Cheap per question, once warm.** The corpus sits behind a 1-hour prompt cache. Measured against the live Worker: about **$3** for the first question after a deploy or a cache expiry, then about **$0.18** for each one within the hour. A review session that stays inside the hour costs a few dollars; the daily brake defaults to $15.
- **Contained.** The Worker and its corpus live under `tools/`, which both deploy legs already exclude, so no repo-only content reaches the public host and the site's FTP/SFTP pipeline is untouched.

**Revisit when** — the monthly spend or the abuse logs (`wrangler tail`) stop looking like a demo tool, or the client review round ends and the passcode should be rotated or the Worker taken down.

**Reflected in.**
- `tools/ucassist-worker/` — Worker, corpus generator, committed corpus, runbook. Deploy-excluded; hand-deployed with wrangler.
- `assets/ucassist-loader.js`, `js/ucassist.js`, `css/ucassist.css` — the launcher and widget (static files, shipped by the normal mirror).
- `js/app.js` — `/* DECISION PD-007 … */` marker beside the loader hook that puts the launcher on `/v2/`, the root twins, and `/legacy/`.
- `assets/site-nav.js`, `index.html`, `design-system/index.html`, `design-system/components/_showcase.js` — the other four launcher hooks.
- `docs/platform-strategy.md` / `.html` — new brief written so platform questions are answered from repo content rather than from the model's general knowledge.

**Source.** Internal tooling decision, 2026-08. Runbook and cost detail in [`tools/ucassist-worker/README.md`](../tools/ucassist-worker/README.md).

### ✅ PD-008 — Model landing Phase 1 cut to four elements  ·  2026-08, developer scoping review
**Decision.** The **Phase 1** model landing page ships **four elements** and nothing else:

1. **Model info header** — machine photo, class, name, description, and two CTAs (buy-or-quote, shop parts for this machine)
2. **Documentation, manuals & videos**
3. **Dispatch a tech** CTA
4. **Related models**

The most-bought parts grid, the compatible-parts grid, the specs card, the hero stat bar, and the machine-lookup band all come out of Phase 1 and move to Phase 2. The interactive click-to-add parts diagram stays a Phase 3 item; Phase 1 ships the exploded view as a static PDF in the documentation grid.

The full-hub design is not abandoned — it stays as the Phase 2/3 target, and every deferred section is left in the prototype and marked, so Phase 2 is an uncommenting exercise rather than a redesign.

**Why.** The four surviving elements share one property: **nothing about them scales with catalog size.** Twenty model pages need twenty hero images and twenty document lists — bounded, parallelizable content work a merch team can finish in a sprint. The two parts grids scale with the *catalog*: the compatible-parts grid needs `compatible_models[]` populated across ~12k SKUs, which the brief already identifies as a 2–3 month structured tagging effort, and the most-bought list needs order history aggregated per model.

Binding the pilot to that tagging effort means **no model landing page ships for a quarter**. Cutting it means the top-20 ship in weeks and produce the traffic data that justifies the tagging spend. The revised Phase 1 needs roughly **eight** category custom fields instead of twenty, and about half a day of content per model once the template exists — down from the ~3 days per model the brief originally scoped.

What the cut gives up is honest and small: a buyer who came to buy a part gets a link rather than a grid, one extra click to the filtered parts list. That filtered list is exactly what the current model URLs already deliver, so Phase 1 remains strictly additive to today's site.

The cut also removes a promise the platform cannot keep. The full-hub prototype's diagram card advertises "click parts to add to cart" — a Phase 3 / Catalyst capability. Phase 1 must not make that offer, so the Phase 1 build ships the exploded view as a PDF download.

**Revisit when** — Phase 1 is live on the top-20 models and two signals land together: the traffic shift from the old `/parts-and-supplies/{brand}/{model}/` URLs confirms the pages are paying off, **and** `compatible_models[]` tagging is underway. The **"Shop parts for this machine" click-through rate** is the specific number to watch: it is the Phase 1 stand-in for the parts grid, and a high rate is the direct evidence that embedding the grid pays for the tagging effort. That tagging is the same data investment [`search-experience-concepts.md`](search-experience-concepts.md) Phase 2 and [`recently-purchased-concepts.md`](recently-purchased-concepts.md) Phase 2 are waiting on — sequence it once for all three. See also [PD-004](#), which is gated on the same part→model mapping.

**Reflected in.**
- `v2/model-tennant-t500e-phase1.html` — **new**, the Phase 1 build. Four elements, no parts grid, no specs card, no stat bar, no machine-lookup band; the exploded-view diagram is a static PDF card. Carries `<!-- DECISION PD-008 … -->` markers on the header, the diagram card, and in the file's opening comment.
- `v2/model-tennant-t500e.html` — kept as the Phase 2/3 target. `<!-- DECISION PD-008 … -->` markers sit on each deferred section (hero stat bar, machine-lookup band, most-bought parts, specs sidecar) with the reason it is deferred; the explainer and a prototype-only phase ribbon cross-link the two pages.
- `docs/model-landing-concepts.md` / `.html` — Phase 1 rewritten to the four elements with a what-ships table and a what-is-deferred-and-why table; TL;DR phase table, Caster-impact pattern list, content-management plan, analytics, and the Catalyst build-cost line all re-split by phase.
- `index.html` — model-landing feature card and the "What's next" gated item updated; the Phase 1 page added to the appendix prototype list.
- `assets/site-nav.js` — Phase 1 page added to the Prototype section menu.
- `docs/backlog.md` — Phase 2 trigger recorded.

**Source.** Developer scoping review, 2026-08. Reasoning and the phase tables in [`model-landing-concepts.md`](model-landing-concepts.md) (§ Phased recommendation).

