skills/validate-profile/SKILL.md
Validate a brand profile end-to-end — required fields, voice/audience completeness, connector reachability, credentials health, and compliance prerequisites — without exposing credential values. Run after any credential change or brand-profile edit.
npx skillsauth add indranilbanerjee/digital-marketing-pro validate-profileInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill is the canonical "is this brand ready to ship work?" gate. It validates a brand profile is complete enough for production use AND that every credential/connector referenced by the profile is actually reachable — without ever printing credential values.
Use this skill:
/digital-marketing-pro:brand-setup (or /digital-marketing-pro:client-onboarding) to confirm the new profile is production-ready./digital-marketing-pro:import-guidelines) to confirm the merge succeeded./digital-marketing-pro:engagement, /digital-marketing-pro:campaign-plan, or /digital-marketing-pro:launch-campaign.For agencies running 50–200 client brands, brand profiles and credentials drift constantly: a junior changes a Slack channel, an API key rotates, a brand voice gets edited. The cost of running a 60-minute engagement on a broken profile is hours of rework. This skill catches those drift cases in under 60 seconds.
The skill is read-only — it inspects state, never modifies it. It also never prints credential values — connector checks emit pass / fail / error-class without echoing the secret.
| Dimension | What's checked | Severity |
|---|---|---|
| Required identity | brand_name, industry, target_jurisdictions non-empty | BLOCKER |
| Voice profile | voice.tone, voice.formality, voice.energy populated | BLOCKER for content work |
| Audience profile | target_audience.primary_persona with role + reading_level | WARNING |
| Guardrails | guardrails.prohibited_terms + guardrails.prohibited_claims non-empty | BLOCKER for regulated industries (pharma, BFSI, healthcare, legal) |
| Compliance jurisdictions | Each declared jurisdiction has a matching rules entry in skills/context-engine/compliance-rules.md | BLOCKER |
| Connector reachability | Every connector named in tracking.backend, integrations.*, analytics.* resolves and authenticates | BLOCKER per failing connector |
| MCP server health | Every entry in .mcp.json (if present) responds to a tools/list ping | WARNING |
| Credential storage | ~/.claude-marketing/{brand}/credentials.json (or env vars) present for every backend referenced | BLOCKER |
| Output paths writeable | ~/.claude-marketing/{brand}/ is writeable; $CONTENTFORGE_PUBLISH_DIR (if cross-plugin) is writeable | BLOCKER |
| Model curator currency | scripts/resolve_model.py --registry-age returns < 90 days | WARNING |
A BLOCKER means "do not let the user run engagement / campaign-plan / launch-campaign until this is fixed." A WARNING is surfaced but does not gate.
If --brand <slug> was passed, use it. Otherwise read the active brand from ~/.claude-marketing/active-brand (set by /digital-marketing-pro:switch-brand). If neither is available, error: "--brand <slug> required, or run /digital-marketing-pro:switch-brand first." Do NOT validate "everything" — validation is per-brand by design.
BRAND_DIR="$HOME/.claude-marketing/{brand}"
test -d "$BRAND_DIR" || { echo "Brand directory not found at $BRAND_DIR — run /digital-marketing-pro:brand-setup first."; exit 1; }
PROFILE="$BRAND_DIR/brand-profile.json"
test -f "$PROFILE" || { echo "brand-profile.json missing under $BRAND_DIR — run /digital-marketing-pro:brand-setup."; exit 1; }
Parse the profile JSON and capture: brand_name, industry, target_jurisdictions, voice.*, target_audience.*, guardrails.*, tracking.backend, integrations.*, analytics.*.
Walk the checklist in the Validation dimensions table above. For each field, record one of: OK / WARN: <reason> / BLOCK: <reason>. Do NOT short-circuit — collect every issue so the user sees the full picture in one pass.
For regulated industries (industry matches any of pharma, pharmaceuticals, bfsi, banking, insurance, healthcare, legal, medical-devices), upgrade guardrails issues from WARNING to BLOCKER.
For every entry in target_jurisdictions, confirm skills/context-engine/compliance-rules.md contains a matching section header (e.g. ### 1.11 India — DPDPA). If a jurisdiction is declared but not covered, BLOCK with: "Jurisdiction {X} declared in profile but no compliance rules for it — engagement will produce non-compliant deliverables."
For each backend referenced in tracking.backend, integrations.crm, integrations.email, integrations.cms, integrations.analytics, integrations.social, run the matching health probe via scripts/connector-status.py:
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/connector-status.py \
--brand "{brand}" \
--connectors "{comma-separated list inferred from profile}" \
--probe-only --no-secrets
connector-status.py --probe-only --no-secrets makes the equivalent of a me/whoami API call and reports HTTP status + auth class (OK / UNAUTHENTICATED / RATE_LIMITED / NOT_FOUND / NETWORK_ERROR). It never echoes the credential value, never writes the credential to logs, and never writes it to the output. If --no-secrets is not supported by the installed connector-status.py, fall back to invoking the connector's MCP tools/list endpoint with the same redaction discipline.
For MCP servers in .mcp.json (if present at the brand or project level), run a tools/list ping against each via mcp__connector__* if the connector is loaded, or invoke a 5-second curl HEAD against the configured url for HTTP MCPs:
for url in $(jq -r '.mcpServers[] | select(.type=="http") | .url' .mcp.json); do
code=$(curl -sS -o /dev/null -w "%{http_code}" -m 5 "$url" || echo "000")
echo "$url -> HTTP $code"
done
HTTP 200, 204, 401 (auth required for GET — POST will work), and 405 (method not allowed for GET — POST will work) all count as "reachable". 404, 000 (DNS / timeout), 5xx count as BLOCK.
test -w "$HOME/.claude-marketing/{brand}/" || echo "BLOCK: brand directory is not writeable"
# Cross-plugin: if ContentForge is installed, check its publish dir too
if [ -n "$CONTENTFORGE_PUBLISH_DIR" ]; then
test -w "$CONTENTFORGE_PUBLISH_DIR" || echo "WARN: CONTENTFORGE_PUBLISH_DIR ($CONTENTFORGE_PUBLISH_DIR) is not writeable"
elif [ -d "$HOME/Documents" ]; then
test -w "$HOME/Documents" || echo "WARN: ~/Documents is not writeable — ContentForge publish copy will fail"
fi
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/resolve_model.py --registry-age
If the registry is more than 90 days old, WARN: "model_registry.json is {N} days old — frontier models change every ~6 weeks. Run scripts/refresh_models.py to check drift." (Do not block — the curator auto-falls-forward on deprecated ids, so an older registry is degraded, not broken.)
Print a structured report. ALWAYS show every check (don't only print failures — agencies need positive confirmation for the rest):
🔎 Brand profile validation — {brand_name}
Slug: {brand} · Industry: {industry} · Jurisdictions: {list}
✅ Required identity brand_name, industry, target_jurisdictions all set
✅ Voice profile tone={tone} · formality={formality} · energy={energy}
⚠️ Audience profile primary_persona.role set, reading_level MISSING
✅ Guardrails {N} prohibited_terms, {M} prohibited_claims (industry={industry})
✅ Compliance jurisdictions EU-GDPR ✓ · IN-DPDPA ✓ · US-CCPA ✓
🛑 Connector — Slack UNAUTHENTICATED (token rotated? re-add via /digital-marketing-pro:add-integration slack)
✅ Connector — HubSpot OK (workspace acme-corp, 1247 contacts)
✅ Connector — Stripe OK
✅ MCP — gmailmcp.googleapis.com HTTP 405 (alive)
✅ Output paths ~/.claude-marketing/{brand}/ writeable; ~/Documents/ContentForge/ writeable
⚠️ Model curator registry is 102 days old — consider scripts/refresh_models.py
Decision: 🛑 BLOCKED — Slack connector unauthenticated. Fix before running:
• /digital-marketing-pro:engagement
• /digital-marketing-pro:campaign-plan
• /digital-marketing-pro:launch-campaign
Re-run /digital-marketing-pro:validate-profile after fixing.
Also emit a machine-readable JSON summary so it can be consumed by /digital-marketing-pro:check, /digital-marketing-pro:status, or downstream automation:
{
"brand": "{slug}",
"decision": "blocked | passed | passed_with_warnings",
"blockers": [{"check": "connector_slack", "reason": "..."}],
"warnings": [{"check": "audience_persona", "reason": "reading_level missing"}],
"passed": ["required_identity", "voice_profile", ...]
}
--no-secrets; if a probe accidentally returns a credential in its error string, redact before printing. The skill output goes to logs and clipboards — assume it leaks./digital-marketing-pro:agency-dashboard --health)./digital-marketing-pro:validate-profile [--brand <slug>] [--json] [--connectors <list>] [--quick]
--brand <slug> — brand to validate (else uses active brand)--json — emit only the JSON summary, no human report (useful for chaining)--connectors <list> — comma-separated subset to probe instead of every connector in the profile (useful when only Slack rotated)--quick — skip the connector reachability probes (run only the field-level checks)brand-setup — interactive brand creationimport-guidelines — bulk-load existing brand guidelines into the profilestatus — unified read-only snapshot of the active brandcheck — pre-publish content quality gateswitch-brand — set the active brandscripts/connector-status.py — the underlying connector health probescripts/resolve_model.py — model curator (used for currency check)tools
Compare two SEO snapshots (GSC, GSC AI Performance, rank tracker, AEO probe) and surface biggest movers per metric — impressions, clicks, position, AI citations. Use when: monthly performance reviews, post-Core-Update triage, AI Mode citation tracking, or before/after content-refresh evaluation.
development
Build a content cluster plan from seed keywords — pillar+spokes architecture with internal-link map, intent grouping, and quality scorecard. Use when: planning topical authority, designing a content hub, deduping cannibalising pages, or staging a programmatic content rollout.
development
One-shot setup that wires Digital Marketing Pro for team usage in Anthropic Cowork. Verifies the Cowork sandbox, checks for a Google Drive integration, creates the canonical Drive folder layout, and confirms team-ready brand-state routing. Use this the first time a Cowork user installs DMP OR when brand profiles aren't persisting across sessions.
development
Find referring domains that link to your competitors but not to you, ranked by an opinionated outreach-priority score with DR / link-overlap / traffic / topical relevance. Use when: planning link-building campaigns, qualifying digital-PR prospects, or running quarterly backlink-gap audits.