# Worked example proof — med_spa brief → plan → validate → dry_run

> **Scope note (2026-08-26):** this report is the live proof for the **v1.0.0** med-spa plan (5 workflows, 28 actions). The examples in this folder are now the **v2.0.0** plan (8 workflows, 99 actions, 32 templates, users, `userRef`, `call_status` trigger). The v2 plan passes `validate_build_plan` and `examples/validate-plan.cjs`; a live `apply_build_plan` dry-run of v2 has not been captured yet and is the next proof to add here.

This is the live proof that the med_spa preset produces a plan that passes the MCP's
authoritative validator and dry-runs clean. Captured 2026-06-25 against the **MCP Testing**
sandbox (a throwaway 1-user account). dry_run writes nothing.

Source artifacts: [`medspa-brief.json`](medspa-brief.json) → [`medspa-build-plan.json`](medspa-build-plan.json).

## 1. `validate_build_plan` (authoritative Zod schema + ref-integrity)

```json
{ "valid": true, "errors": [], "warnings": [], "referencesScanned": 48 }
```

Every one of the 48 symbolic ref-pointers resolves to a defined object of the right
namespace. Zero dead refs, zero warnings. The structural guarantee holds: no action can
point at an ID that does not exist.

## 2. `apply_build_plan` mode:"dry_run" — summary

```json
{
  "wouldCreate": 40, "existing": 0,
  "workflowsTotal": 5, "workflowsGated": 3,
  "actionsExpanded": 28, "actionsManual": 0, "actionsNeedContent": 0
}
```

Definition of done met: **0 actionsNeedContent, 0 actionsManual**, and every workflow
trigger auto-built (`triggerAutoBuilt: true` on all 5). `calendarsManual: []` — the
round_robin consult calendar auto-builds because the account has exactly one user (the
solo operator is auto-assigned as staff).

### Per-workflow expansion

| Workflow | Trigger (auto-built) | Actions | Gated DRAFT by |
|---|---|---|---|
| Speed to Lead | form_submission | 6 | handoff.a2p (sends SMS) |
| Lead Nurture | contact_tag | 4 | — (email-only, ungated) |
| Replied - Stop and Route | customer_reply | 3 | — |
| New Patient Onboarding | appointment (confirmed) | 6 | handoff.a2p (wait_appointment + SMS) |
| No-Show Win-back | appointment (noshow) | 9 | handoff.a2p (find_opportunity branch → SMS) |

The No-Show Win-back exercises `find_opportunity` as the last action: 9 native nodes
(head + 2 transitions + found[update_opportunity, send_sms] + notFound[send_email]).

### Part 2 — "you must do these by hand" (expected, not failures)

- Design + populate the GHL funnel pages (Blueprint builds funnel + named steps;
  page content is the operator's, filled from `funnel-page-content-template.md`).
- [OPERATOR-UI] Connect your calendar (produces the booking link).
- [OPERATOR-EXT] Register A2P 10DLC (holds the 3 SMS-bearing workflows DRAFT until met).
- [OPERATOR-EXT] Connect Stripe.

These are the honest, designed handoffs — the second half of the approval view, not
unexpected manual steps.

## Notes for re-running

- The committed `medspa-build-plan.json` carries `internal_notification.to:
  "REPLACEwithGetUsersId"` (a placeholder so the shipped example contains no real account
  id). The live dry_run above substituted the demo account's real operator user id
  (resolved via `get_users`), which is the one field that legitimately holds a real GHL id
  (Blueprint never creates users). The skill resolves the subscriber's own id at plan-gen
  time. Both forms dry_run identically clean.
- `execute` mode was intentionally not run: the DoD for the plan is validate + dry_run
  clean before the approval gate. Live execution is the operator's post-approval step.
