The universal GM dashboard, margin engine, efficiency/coaching logic and staff timer all run a nail salon by swapping two config layers — nail-INDUSTRY + Ooo La La-BRAND. Zero universal-core logic was rewritten. "Don't build anything for Ooo La La we're not building for Bark & Purr" holds: it's the same core B&P runs, re-tenanted.
The line between these two columns IS the IP. Everything on the right lifts unchanged to the next tenant.
| Dimension | Bark & Purr (grooming) | Ooo La La (nail) | Layer |
|---|---|---|---|
| Subject | the pet — breed · coat · size | the guest — a person | config |
| Service driver | dog size × coat → time/price | service type + add-ons → time/price | config |
| Capacity unit | groomer-hours | station-hours (mani + pedi chairs) | config |
| Labor lanes | commission groomer + hourly bather | nail tech: commission / hourly / booth / hybrid | config |
| Efficiency model | bath + groom phase minutes | per-service active minutes | config (effModel flag) |
| Margin math | min × loaded $/min → margin | identical | core |
| Coaching engine | flag ≥15 pts slower, n≥4 | identical — flagged Bao (new) | core |
| 3-leg layout + honesty | revenue/profit/service | identical | core |
Open with the "Sample data" toggle ON to see them populated (industry averages — clearly watermarked SAMPLE).
Per Stack Protocol §"flag the leak" — the swap surfaced refactor debt. Naming it is the point.
effModel, subject, coachCauses, phase labels). It renders both grooming and nail from config — that's the paid-down version. Recommendation (Tier-2): converge the v2 B&P dashboard onto gm-core.js so there's one core, not an inline grooming monolith + a generalized fork.Every benchmark, pay-model split and margin band is cited. No invented numbers.
| Config file | What's in it | Key sources |
|---|---|---|
| active-time-benchmark.json | active minutes per service (mani 30 · gel 45 · dip 60 · acrylic set 75 · pedi 55) + add-ons + role-tier modifier | Polish Pops, BTArtbox, Bela, Booksy |
| pay-models.json | hourly $10–25 · commission 30–60% (entry 40/60) · booth $400–1,200/mo · hybrid (gold standard) | GlossGenius, Zenoti, Square, Strategies.com, Booksy |
| economics.json | net margin 15–25% · labor 45–55% · rent 8–10% · supplies 5–10% · seat utilization 75–80% · capacity = stations | Booksy, BusinessDojo, PlanPros, Vagaro |
| service-catalog.json | nail menu + add-ons (baseline pricing) | known-salon baseline + Frisco menus |
Folder: industries/nail-salon/ · brand layer: industries/nail-salon/brands/ooolala/. All figures are industry baselines for the demo; a real salon's menu, pay structure and P&L drop straight into these files and every page recomputes.
Per Linda: improvements go into the universal core; both tenants inherit by config. We do not hand-maintain two dashboards.
The nail instance is expressed in the canonical core's exact config contract (gm/v2-config.nailsalon.ooolala.js) and validated against the core's verbatim margin math in node:
| Check (run on the core's own functions) | Result |
|---|---|
| Per-service net margin | 15–25% — lands in the industry band, no core edit |
| Slow-tech acrylic (99m vs 75m bench) | attributed to TIME over benchmark, not price — the pricing-vs-staffing split works on nails |
| Frisco competitive verdicts | compute (gel/acrylic/pedi "priced right"; classic mani thin → "lower cost") |
| Margin math change required | zero — nails = one commission lane with prep-phase 0 |
| Only true leak found | display copy — "Bath/Groom" labels & cause text → move to config |
Convergence (Tier-2, handed to GM-dashboard seat): lift the canonical core into a shared file + add a laborModel switch + move the grooming display strings to config. Then B&P and Ooo La La are each just a config bundle on one engine. Ref: captains-log/HANDOFF_GMcore_Externalize_for_Nail_Swap_20260612.md
Today: 100% SAMPLE / "connect this source". Each source flips by setting one config key (null → his URL); that source's SAMPLE block is replaced by his real data. No core or page edits.
| Source | Powers | Flip | Needs from Kareem |
|---|---|---|---|
| MyTime / booking | revenue, ticket, no-shows, utilization, repeat mix | sources.mytime.endpoint null→URL | read access + location ID |
| POS / P&L | net margin, labor %, true-cost, per-service flags | truecost.isSample true→false + his P&L | bookkeeping read + pay structure |
| Compass | active time vs benchmark, coaching | sources.compass.endpoint null→URL | techs use the timer |
| Analytics | search→booking conversion | sources.ga4 property + endpoint | GA property read |
| Reviews | rating trend | sources.reviews.endpoint null→URL | Google Business Profile |
No Kareem needed for: active-time benchmarks, pay-model engine, margin bands, Frisco competitive set, and the swap itself — all ship today. Full detail: industries/nail-salon/brands/ooolala/go-live-manifest.json.
Sharpen-the-saw: reverse-engineered failure modes before shipping.
| Pitfall a skeptic raises | How it's handled |
|---|---|
| "This is just made-up numbers." | Every config figure is cited (Booksy, Zenoti, GlossGenius, Square, trade timing guides). SAMPLE is watermarked; demo toggle OFF by default; truecost.isSample shown on-page. |
| "The ticking timer looks live." | Labelled "illustrative sample timer"; source note says SAMPLE captures until Compass launches. |
| "You forked the dashboard — now you maintain two." | Flagged honestly. The nail config is drop-in to the canonical core; convergence handoff filed so it's ONE core, not a fork. |
| "Grooming logic won't really fit nails." | Validated on the core's verbatim math: margins, slow-tech attribution, competitive verdicts all compute. Only display copy leaks. |
| "Frisco competitor prices aren't his." | Labelled SAMPLE public-menu ranges; verdict direction holds across the range; firmed up without needing Kareem. |
| "What happens when his real numbers differ?" | Nothing breaks — they replace the SAMPLE block via config; every page recomputes. That's the design. |