# Marketing-skills audit — anwarpy.ai (2026-10-10) > Copy of the local audit of 2026-10-10 (source: agent workspace `marketing-skills-audit-findings.md`), committed for the launch PR. Items marked ❌ were fixed the same day in the launch/early-access branch; see `docs/launch/2026-10-10-launch-readiness.md` for per-item evidence. Audited against installed playbooks: paywalls, signup, ai-seo, programmatic-seo, aso (`.agents/skills/`). Evidence: repo `web/src` (file:line) + live production HTML. Verdicts: ✅ compliant · ⚠️ partial · ❌ gap. ## Paywalls (skill: paywalls v2.0) **Current state — technically solid.** Gating = `users.role` (free/vip/admin); VIP body has field-level read ACL (`collections/Posts.ts:57-71`) + page-level select — never served to non-VIP. Teaser paragraphs required for VIP posts (`Posts.ts:39-51`) with fade-out mask; paywall box on locked posts has headline, price-in-CTA («اشترك بـ $10 شهرياً», `lib/format.ts:11`), objection line («تلغي في أي وقت»), login secondary. Per-file byte gate on attachments (`/api/files/[id]`). Stripe: Checkout → `/payment-success` re-verification + webhook activation; deletion webhook downgrades. No dark patterns anywhere. **Gaps** - ❌ Value demonstration: locked H2 titles never shown (`posts/[slug]/page.tsx:202` extracts TOC only when unlocked) — paywall promises «الأقسام العملية» generically. - ❌ Social proof absent on paywall and subscription (subscription shows guide counts only). No fabricated quotes allowed; needs owner-provided testimonial or real aggregate. - ❌ No FAQ link or free-escape-hatch inside the paywall box (FAQ lives only on /subscription). - ❌ Coming-soon discovered after failed click (`subscription/subscribe-button.tsx:16-27`): glossary says disabled «قريباً» button; actual renders active «اشترك الآن» and reveals coming-soon only after POST fails. - ❌ No measurement (paywall impression/CTA events) — blocked on analytics-tooling owner decision. - ⚠️ Pricing: monthly $10 only; skill suggests annual toggle — owner policy. - 🧹 Dead code: `styles/post-locked.css`, `styles/post-unlocked.css` imported nowhere (live styles in globals.css:168-190, design.css:271-287). ## Signup (skill: signup v2.0) **Current state.** 5 fields (الاسم required, email, password ≥8 with show-toggle + hint, تأكيد كلمة المرور, terms checkbox) + hidden `termsVersion`. Native constraint validation with Arabic custom messages; paste allowed; visible labels; single column mobile. Post-submit: auto-login → `/verify-email` (can explore unverified; only commenting gated, 403 with Arabic copy) → `/onboarding` preserving `anwarpi:signup-next`. `safeNext` redirect hygiene. Rate limits: 5/h signup, 10/15min login per-IP (`middleware.ts`), Payload lockout 5/15min (admin exempt, PBKDF2 timing-equalizer). **Gaps** - ❌ **Verification email is never sent at signup** — `sendVerificationEmail` has a single call site (`api/auth/resend-verification/route.ts`); `Users.ts` has no afterChange hook. The verify page claims «أرسلنا رابط التأكيد إلى:» falsely until the user clicks resend. Highest-impact fix. - ❌ 429 body mis-parsed: signup reads only `err?.errors?.[0]?.message` (`signup/page.tsx:44-47`), so middleware's `{error}` JSON is ignored → rate-limited users see «قد يكون البريد مستخدماً بالفعل» (wrong). Login shows one generic message for all failures (`login/page.tsx:29-31`). Retry-After never surfaced. - ❌ No spam-folder step and no change-email affordance on `/verify-email` (skill post-submit checklist). - ❌ Field count above skill's ≤3 guidance (confirm-password cuttable; name kept for comment display names). - ❌ No «مجاناً — بلا بطاقة ائتمانية» / privacy microcopy near CTA; no inline duplicate-email error with login link; no email-typo suggestion; static (not live) password hint. - ❌ Social auth absent (Google/Apple) — needs owner (OAuth apps). - ✅ Already compliant: single email field, no placeholder-only labels, value-story panel, terms timestamped (`termsAcceptedAt`). ## AI-SEO (skill: ai-seo v2.0) **Current state — good SSR baseline.** All pages force-dynamic server components (content in initial HTML); `lang="ar" dir="rtl"`; self-canonical everywhere; title template; sitemap (published posts + lastmod, 8 static paths); robots with disallow list + sitemap pointer; WebSite JSON-LD (home) and Article JSON-LD (posts); answer-first seed content; public per-post JSON at `/api/posts/[slug]`. **Gaps** - ❌ No `llms.txt` / `llms-full.txt`. - ❌ No named AI-crawler robots policy (implied `*` only); `/api` disallow hides the agent-readable post JSON. - ❌ Article pages emit **no og:image** — `generateMetadata` (`posts/[slug]/page.tsx:16-33`) defines `openGraph` which *replaces* the root OG object (no siteName/locale/images), though `post.cover.url` exists. - ❌ Draft leak: the metadata query lacks the `status: 'published'` filter the page query has → draft titles/deks render in metadata of 404 pages. - ❌ Article JSON-LD missing `image`, `dateModified`, `description`, `mainEntityOfPage`, `publisher`. - ❌ FAQPage schema absent despite real Q&A (`subscription/page.tsx:16-21` FAQ_ITEMS); BreadcrumbList absent despite breadcrumb UI (`posts/[slug]/page.tsx:179-183`); no Organization schema. - ❌ Lexical renderer has no `table` node case (~`page.tsx:373-387`) — comparison tables in content fall through to `

`. - ⚠️ Freshness: sitemap uses updatedAt but the page shows only publishedAt — add «حُدّث» when they differ. - ✅ Answer-first style present; single H1; h2 extraction; agent-accessible free JSON exists. ## Programmatic SEO (skill: programmatic-seo) **Current state.** Indexable surface = `/`, `/articles`, `/posts/[slug]`, 7 static pages. All category/topic navigation is client-side DOM filtering on ONE URL (`articles-client.tsx:11-19, 62-70`) — clicks never update the URL; `?topic=` states share one canonical. `/catalog` permanently redirects to /articles; `catalog-filters.tsx` + `catalog-deck.tsx` exported nowhere (dead). **Data available in Postgres** (Posts): title, slug, dek, category (6 values), level (3), access, status, publishedAt/updatedAt, readingMinutes, views, likes, cover, attachments. Public taxonomy already exists: `lib/topics.ts` — 4 topics (`agents` الوكلاء, `models` النماذج, `automation` الأتمتة, `foundations` الأساسيات) mapped from the 6 CMS categories via `topicsFor(post)` + `isTopic()`. **Decision (owner-confirmed 2026-10-10): build 4 topic hub pages `/topics/[topic]`** — server-rendered directories with unique intros, counts, breadcrumbs + BreadcrumbList, sitemap entries, internal links from home/articles/post breadcrumbs. Later candidates (not now): topic×level personas (gate on ≥3 posts/combo), templates directory, glossary (needs new field). Comparisons/locations/translations: no data, N/A. ## ASO (transferable items only — site, not an app) - ❌ Home title is brand-only «أنور بآي» — unknown brands need keywords: «أنور بآي — أدلة الذكاء الاصطناعي العملي بالعربية». - ❌ Only root + posts have descriptions; give /articles, /subscription, /about their own Arabic descriptions. - ❌ One static OG image for everything → per-post `cover` as og:image (same fix as ai-seo list). - ❌ Surface «حُدّث» freshness date on articles. - ✅ Localization correct (ar locale, RTL, og:locale). Ratings/screenshot fields: N/A. ## Needs owner (not in this pass) 1. Social auth providers (Google/Apple or magic links) — OAuth apps + keys. 2. Analytics/experimentation tooling (paywall impressions, CTA CTR, form drop-off) — vendor + privacy stance; code ready once chosen. 3. Annual plan / money-back guarantee — pricing policy. 4. Testimonial content — must be real; nothing fabricated. 5. Brand spelling inconsistency: site uses «أنور بآي» everywhere (title, og:site_name, JSON-LD); docs use «أنور بي آي». Pick one canonical spelling. 6. Policy: allow free users to comment on VIP posts (currently hard-403) — easy conversion lever if allowed. 7. Sitemap `limit: 500` with no pagination — fine at 13 posts; revisit at scale. /login + /signup kept in sitemap. 8. `GLOSSARY.md` says free users are *redirected* from /payment-success; actual behavior is an in-page retry state — doc will be aligned to code (no redirect; no VIP minting either way).