← back to Philipperomano
REVIEW-2026-05-04.md
64 lines
# philipperomano — overnight debate-team review (2026-05-04)
**Light review** — 224-LOC single-file Express server, snapshot-driven (NOT live dw_unified), DW-spec compliant, NO command54 leaks. Inline review (no parallel-agent fan-out needed for 224 LOC).
## Patches APPLIED (1 — minimal)
| File:line | Change |
|---|---|
| `server.js:14` | Added 3 security response headers via tiny middleware: `X-Content-Type-Options: nosniff`, `X-Frame-Options: SAMEORIGIN`, `Referrer-Policy: strict-origin-when-cross-origin`. No helmet dep — keep this storefront tiny. CSP deferred (would need `'unsafe-inline'` for the inline `<style>` block). |
`node --check` clean.
## DW-spec compliance audit ✅
| Spec rule | Status |
|---|---|
| Read products from `dw_unified.shopify_products` snapshot, not live | ✅ `data/products.json` (8,227 SKUs) |
| Memo-sample CTA on every product card + detail page | ✅ "Order Memo" on cards (line 167), "Order Memo Sample (free)" on detail (line 211) |
| Memo-sample link → `designerwallcoverings.com/products/{handle}#sample` | ✅ `memoSampleUrl()` line 41 |
| `command54` MUST NEVER appear in customer content | ✅ Audited — 0 occurrences in server.js + products.json (8,227 records) |
| All titles end with "Phillipe Romano" branding | ✅ "Aegean - Seafoam Wallcovering \| Phillipe Romano" |
| Footer credits Designer Wallcoverings | ✅ Line 132: "private-label collection of Designer Wallcoverings" |
| Loopback bind by default | ✅ `BIND=127.0.0.1` env-overridable (line 233) |
| HTML-escape all user/DB strings | ✅ `esc()` used consistently (q, type, title, dw_sku, mfr_sku, image_url, handle) |
| No DB writes / no Shopify writes | ✅ Read-only static server |
| No admin routes | ✅ Just `/`, `/p/:handle`, `/health` |
## Findings (none P0)
| Severity | Finding | Notes |
|---|---|---|
| **P2** | `parseInt(req.query.page)` lacks radix | Defaults to base 10; no behavior bug. Lint nit. |
| **P2** | `JSON.parse(fs.readFileSync(products.json))` at module load | If file is malformed at startup, server crashes. No graceful fallback or last-known-good cache. Adopt try/catch + exit-with-clear-error. |
| **P2** | Each search request rescans full 8,227-row array in-memory | Acceptable today; if traffic grows, build a precomputed lowercased haystack on startup. |
| **P2** | `/health` exposes `DATA.length` + `TYPES` list | Minor info disclosure. Acceptable since both are intended public-facing facts (the home page also says "Search 8,227 SKUs"). |
| **P2** | No CSP header | Inline `<style>` requires `'unsafe-inline'`; CSP would be limited until styles extract to a static file. Deferred. |
| **P2** | No rate limiting | Read-only static server with loopback bind — Cloudflare in front handles this layer. |
## What's NOT here (good)
- No `.env` (no secrets — server reads zero env vars except PORT + BIND, both non-sensitive)
- No git history concerns
- No SSRF vectors (no outbound fetch)
- No SQL (no DB)
- No file uploads
- No admin/auth surface to harden
- No webhook handlers
## Architecture verdict
**NOT a directory-core / factory-core / drops-core / place-graph-core consumer.** This is a thin static read-only catalog viewer — the canonical seed of the `/dw-site-build` skill (per MEMORY: "First site through this pipeline. Source: `~/Projects/philipperomano/`").
It's already in the architectural shape Steve's been refining as the universal DW vendor storefront pattern. The architectural recommendation is the inverse: **anything new added here should reflect back into `/dw-site-build` SKILL.md** so the next vendor build (novasuede, architectural, etc.) inherits it for free. Per MEMORY `feedback_dw_site_build_universal.md`.
### Gaps vs current `/dw-site-build` spec (worth back-porting)
1. **Dark/light toggle** — per MEMORY `feedback_dark_light_toggle_standard.md` ("every customer-facing Steve site ships dark/light"), philipperomano should have one. The cream/sienna palette here would invert nicely. **NOT patched** because it's a UX feature, not a security/correctness fix. Steve's choice whether to add now or wait until /dw-site-build is updated to include the toggle reference for next vendor build.
2. **GA4 (Google Analytics) injection** — per `/analytics` skill standing rule, every new site gets GA4 from day one. Couldn't audit without checking `~/.config/ga-analytics-agent/properties.json` for a `philipperomano.com → G-XXX` mapping.
3. **Cache-Control headers** — products are a snapshot; setting `Cache-Control: public, max-age=3600` on `GET /` and `GET /p/:handle` would cut load + latency. Cloudflare would serve from edge.
## Files touched
- `/Users/stevestudio2/Projects/philipperomano/server.js`