# CLAUDE.md

Project constitution. Claude Code must read this file at the start of every session and follow it without exception.

---

## 1. WHAT THIS PROJECT IS

A production-ready MVP e-commerce store for a Pakistani bedding brand: bedsheets, blankets, bedding sets and pillows.

Stack: WordPress + WooCommerce + MySQL.
Hosting: Namecheap shared hosting (cPanel, LiteSpeed, PHP 8.2, no root, no Redis).
Primary payment: Cash on Delivery.
Market: Pakistan. Currency PKR. Language English, translation-ready for Urdu.
Design: custom classic theme written from scratch. No page builder. No React.

This is not a generic WooCommerce install. It is a branded storefront with a custom operations layer for COD verification, fulfilment and courier tracking.

---

## 2. THE BRAND NAME IS NOT SET YET

Placeholders are used throughout the code and docs:

| Placeholder | Meaning |
|---|---|
| `brand-theme` | theme directory and slug |
| `brand-commerce` | business plugin directory and slug |
| `bc_` | PHP function and class prefix (`bc_`, `BC_`) |
| `_bc_` | order and product meta key prefix |
| `bc_` | database table prefix segment (`{$wpdb->prefix}bc_settlements`) |
| `brand` | text domain |
| `Brand Name` | display name in copy |

When the real name arrives, follow `RENAME.md`. Do not invent a name. Do not start renaming without being asked.

---

## 3. NON-NEGOTIABLE RULES

1. **Business logic lives in the plugin, presentation lives in the theme.** If deleting a file would corrupt or orphan an existing order record, it belongs in `brand-commerce`, not `brand-theme`. No exceptions.
2. **HPOS is on.** Never use `get_post_meta` or `update_post_meta` on an order. Always `$order->get_meta()`, `$order->update_meta_data()`, `$order->save()`. Never query orders with `WP_Query`. Use `wc_get_orders()`.
3. **No new plugin without a written justification** in `docs/07-DECISION-LOG.md`. The approved list is in section 6 of `docs/05-TECHNICAL-ARCHITECTURE.md`. Anything not on it needs a decision entry first.
4. **Escape late, sanitise early.** Every echo is escaped by context. Every input is sanitised by type. Every state-changing request has a nonce AND a capability check. A nonce alone is not authorisation.
5. **No build tools run on the server.** Compiled CSS and JS are committed to `assets/dist/`. The server never runs npm.
6. **No em dashes in any output**: code comments, copy, docs, commit messages, admin strings. Use a comma, a colon, a semicolon, parentheses, or a new sentence.
7. **Build incrementally.** One phase at a time from `prompts/`. After each phase: test it, fix it, document what changed, then stop and report. Do not run ahead into the next phase.
8. **Scope discipline.** If a requested feature is not required for purchase, product management, order management, payment, fulfilment, delivery or basic analytics, label it `FUTURE FEATURE` and do not build it. Say so out loud rather than quietly implementing it.
9. **No fake urgency, fake discounts, fake stock counters or invented reviews.** Trust signals must be true or they do not ship.
10. **Never store card data.** Never commit a credential. Gateway keys and API tokens live in `wp-config.php` constants only.

---

## 4. CODE STANDARDS

- WordPress Coding Standards, enforced by `phpcs.xml` (WordPress-Extra plus WooCommerce sniffs). Run `composer run lint` before declaring a phase done.
- PHP 8.2 target. Typed properties and return types where WordPress hook signatures allow.
- Class files: `class-{name}.php`, one class per file, loaded by the plugin autoloader. No `require` chains scattered through the code.
- Text domain on every user-facing string. No hard-coded English in PHP outside translation functions.
- Front-end JavaScript is vanilla ES2018, no jQuery except where WooCommerce forces it (variations, checkout, add to cart).
- CSS uses the custom properties defined in `docs/04-DESIGN-SYSTEM.md`. No hard-coded hex values, no magic pixel numbers outside the spacing scale.
- Never edit WooCommerce core templates in place. Override only the templates listed in the technical architecture, and prefer unhooking and rehooking over overriding.

## 5. PERFORMANCE BUDGET, ENFORCED

Two different budgets, do not confuse them.

**Our own compiled bundles**, enforced by `npm run size`: CSS under 45 KB minified, JS under 25 KB minified.

**Total page weight**, everything including WooCommerce's own assets, fonts and images, measured in Lighthouse:

| | Homepage | Product page |
|---|---|---|
| Total transfer | 900 KB | 800 KB |
| CSS, all sources | 60 KB | 60 KB |
| JS, all sources | 90 KB | 110 KB |
| LCP target, throttled 4G | under 2.5s | under 2.5s |
| CLS | under 0.05 | under 0.05 |

If a change pushes a page over budget, it does not ship. Report the overage instead of shipping it quietly.

## 6. SECURITY RULES IN CODE

| Context | Sanitise on input | Escape on output |
|---|---|---|
| Plain text | `sanitize_text_field` | `esc_html` |
| Textarea | `sanitize_textarea_field` | `esc_textarea` |
| Attribute | `sanitize_text_field` | `esc_attr` |
| URL | `esc_url_raw` | `esc_url` |
| Email | `sanitize_email` | `esc_html` |
| Integer | `absint` | `esc_html` |
| Money | `wc_format_decimal` | `wc_price` |
| Rich text | `wp_kses_post` | `wp_kses_post` |
| Key or slug | `sanitize_key` | `esc_attr` |
| Pakistani phone | `BC_Phone::normalise()` | `esc_html` |

AJAX and REST pattern, every time:
1. Verify nonce.
2. Check capability (`edit_shop_orders`, `manage_woocommerce`, `edit_products`). Never `is_admin()`.
3. Whitelist and sanitise each expected input by name.
4. `wp_send_json_success` / `wp_send_json_error`.

Never return a bare `true` from a REST `permission_callback`. Add `wp_ajax_nopriv_` only when the endpoint is deliberately public.

**Three endpoints are deliberately public**, because anonymous visitors need them: `brand/v1/cart-count`, the listing Load More route, and the province-to-city lookup. Each still needs a named `permission_callback` function that returns `__return_true` **with a comment stating why it is public**, plus its own rate limit, plus read-only behaviour. A public endpoint that writes anything, or that can leak another visitor's data, is a bug. Anything not on this list of three is private.

## 7. HOW TO WORK THROUGH THIS PROJECT

The build is split into phases in `prompts/`. Each prompt file is self-contained: scope, deliverables, acceptance criteria and an explicit stop condition.

Run them in order. At the end of each phase:

1. Verify every acceptance criterion, out loud, one by one.
2. Update `docs/PROGRESS.md` with what was built and anything that changed from the spec.
3. If a spec decision turned out wrong, add an entry to `docs/07-DECISION-LOG.md` explaining the change and why. Do not silently deviate.
4. Stop. Wait for the go-ahead on the next phase.

## 8. DOCUMENT MAP

| File | What it settles |
|---|---|
| `docs/00-ORIGINAL-BRIEF.md` | the client's own words, the source of truth for intent |
| `docs/01-BUSINESS-REQUIREMENTS.md` | business rules, personas, order lifecycle, MVP boundary |
| `docs/02-INFORMATION-ARCHITECTURE.md` | sitemap, taxonomy, attributes, URLs, navigation |
| `docs/03-UX-SPECIFICATION.md` | page-by-page layout, checkout spec, microcopy |
| `docs/04-DESIGN-SYSTEM.md` | tokens, components, image standards |
| `docs/05-TECHNICAL-ARCHITECTURE.md` | file tree, hosting constraints, plugin list, build pipeline |
| `docs/06-DATA-MODEL.md` | every meta key, custom table and status slug |
| `docs/07-DECISION-LOG.md` | locked decisions with reasoning, and where the brief was overruled |
| `docs/08-QA-TEST-PLAN.md` | the checklist that gates launch |
| `docs/09-LAUNCH-RUNBOOK.md` | staging to live on Namecheap |
| `docs/10-OPEN-QUESTIONS.md` | what the client still has to answer |

When the brief and a spec doc disagree, the spec doc wins, because the spec docs record deliberate decisions made against the brief. Those overrules are listed in the decision log.
