brand-and-content-system
Extract real brands (Wayback for rebuilds). Copy system, headline/CTA rules, trust surfaces, legal pages, SEO+structured data, anti-AI-slop, microcopy, DESIGN.md, W3C DTCG tokens, pSEO 5 types, GEO/AI search.
Install
npx skills add https://github.com/heymegabyte/claude-skills/tree/master/09-brand-and-content-system
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install heymegabyte-claude-skills@llmmart
git clone https://github.com/heymegabyte/claude-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole heymegabyte/claude-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
09 — Brand and Content System
Extract real brand assets and apply the full copy system (anti-slop, SEO, trust surfaces, GEO) to every website build surface.
Model migration note (pass-77, 2026-06-09):
DALL-E→ GPT Image 1.5 +GPT-4o→ GPT Image 2 vision. Perplatform.openai.com/docs/deprecations. Brand extraction protocol unchanged.
Brian's Brand Voice
- Slogans: "Open-Source Wizardry. 100% Wizardry. 0% Robes." / "Often imitated, never duplicated."
- Newsletter: "Lab Insights Journal" | Handle: @HeyMegabyte | Email: hey@megabyte.space / brian@megabyte.space
- Tone: professional but irreverent, "Hey" not "Hi", first-person Megabyte Labs
- Always "cross-platform" + "open-source" | Install Doctor: "single command" / "one-liner"
- Hero: "[Topic] Innovation" | Footer CTA: "Let's Talk" | Rates: $140/hr ($70 nonprofit), $100/mo WordPress
- Social: all platforms, "Megabyte Minis" YouTube, Dev.to, Patreon
- Psychology: reciprocity (teach), social proof near CTAs, authority (depth/numbers), unity ("we/us"), Peak-End Rule
Brand Extraction (Rebuilds)
Screenshot existing (Wayback if down). Extract logo/colors/fonts/tone. Never discard equity.
Color extraction (NON-NEGOTIABLE) — Screenshot with Playwright, GPT Image 2 vision extracts hex (logo priority), cross-ref logo, build palette, validate WCAG AA. NEVER invent, NEVER use Emdash defaults for clients, NEVER infer from category.
Second-pass verification (BUILD-BREAKING — pre-deploy) — After GPT Image 2 vision returns
{primary, secondary, accent}:- Load logo PNG via sharp, sample dominant chroma via k-means k=5 ignoring transparent + near-white/near-black (top-3 cluster centroids in HSL)
- For EACH color, compute min HSL hue-distance to top-3 logo chromas — if
min_hue_distance > 30°AND saturation>0.2, FAIL w/ diagnostic - Re-run GPT Image 2 vision w/ corrective prompt naming top-3 logo chromas + demanding
primaryderived from one - Validator:
validate-color-from-logo.mjsinbuild_validators.tsbetween brand-research and template-pick - NEVER ship primary color failing hue-distance check
Logo-luminance + source-theme drives theme (NON-NEGOTIABLE) — Two-signal:
- Signal A: logo dominant-color luminance (WCAG formula)
- Signal B: source-site dominant background luminance (Playwright screenshot of
bodybackground) - BOTH agree → match; disagree → source-site wins UNLESS source design score <7/10 (then logo wins)
- High-quality source (≥7/10 via GPT Image 2 vision
detail:low) → match source theme verbatim - Set theme BEFORE template selection. Reject palette where logo-on-bg contrast <4.5:1 (WCAG AA).
- "Dark-first" applies to accent-rich Emdash/SaaS brands, NOT to logo-driven non-profit/serif clients or high-quality light-themed source brands
Logo-vs-container contrast (BUILD-BREAKING — every render) — Every logo render (header, footer, hero, modal, splash, mobile menu, sidebar) MUST contrast container bg by ≥4.5:1 on logo's dominant chroma (not transparent pixels)
- Forbidden: white-text-logo on white/cream | dark-text-logo on dark/navy | low-saturation-logo on same-hue bg
- Resolution: header AND footer themes chosen AFTER logo luminance scan. Dual-theme site needing SAME logo → ship TWO files (
brand-mark-light.svgfor dark bg,brand-mark-dark.svgfor light bg) + CSS<picture><source media>swaps - Automate via skill 12 logo-variant-generator (Real-ESRGAN inversion or
magick -channel RGB -negate; color logos w/ text → GPT Image 1.5 w/ "same logo on transparent bg w/ text inverted to - Validator:
validate-logo-contrast.mjs— Claude Sonnet 4.6 vision samples logo bbox + container computed bg at 6bp + pixel sampling, fails if <4.5:1
Brand-element extraction (logo is gold mine — extract DNA) — GPT Image 2 vision returns
{font_family_guess, suggested_heading_font, suggested_body_font, font_weight, letterspacing, has_icon_mark, icon_mark_description, icon_mark_dominant_color, decorative_motif_description, motif_extractable (bool)}- Matched Google Font →
--font-headingsite-wide motif_extractable=true→ crop icon-only region (magick logo.png -alpha extract -trim +repage), upscale 2-4× via Real-ESRGAN / GPT Image 1.5 variation, save asassets/brand-splash.png+assets/brand-mark.png
- Matched Google Font →
Logo singularity (BUILD-BREAKING — exactly ONE logo file per container) — Every logo container renders EXACTLY ONE logo asset
- Never composite two logo sources side-by-side; never stack icon-mark + wordmark as separate
<img>tags; never renderapple-touch-icon.pngnext tologo.svgin same container - Composition at asset-prep time via
magick logo-mark.png logo-wordmark.png +append, NOT at render time - Validator:
validate-logo-singularity.mjsparsesdist/HTML; count>1 descendant<img>or inline<svg>in same logo container = FAIL
- Never composite two logo sources side-by-side; never stack icon-mark + wordmark as separate
Logo (NON-NEGOTIABLE) — Every project needs premium logo. See Skill 12 for full process.
Audit — logo found + rated ≥7/10 + works 16-512px, colors EXTRACTED, palette WCAG AA, typography + tone + messages identified.
Brand Extraction from Physical Assets (LOCAL BUSINESS — NO WEBSITE)
- Street View Static API:
https://maps.googleapis.com/maps/api/streetview?size=1200x800&location={lat},{lng}&source=outdoor - Places photos: filter
types: ["exterior", "storefront"] - GPT Image 2 vision on storefront — prompt:
"Extract brand identity from this business storefront photo. Return JSON: {sign_text, sign_font_style, primary_color (hex), secondary_color (hex), accent_color (hex), logo_description, overall_aesthetic, confidence (0-1)}" - Business cards: GPT Image 2 vision extracts logo (crop region), colors (exact hex), font, tagline, NAP for verification.
- Color extraction priority: 1. Signage → 2. Logo → 3. Storefront awning/trim → 4. Interior decor → 5. Vehicle wrap → 6. Business card → 7. Category default (LAST RESORT). Each tagged with
color_source. - Font matching from signage: Script → Dancing Script | Serif → Playfair Display | Modern sans → Inter | Hand-lettered → Caveat. Never use exact proprietary fonts.
Brand Inference (New Products)
- Dev tool → technical/dark/monospace | SaaS → professional/clean/cards | Agency → confident/bold
- E-commerce → friendly/product-focused | Nonprofit → warm/impact imagery | API → technical/docs-forward
- Emdash defaults (NOT for clients):
#00E5FF,#50AAE3,#060610. Sora / Space Grotesk / JetBrains Mono.
Anti-AI-Slop Detection (MANDATORY SCAN)
Banned copy words: delve | leverage | unleash | revolutionize | best-in-class | cutting-edge | discover | innovative | seamless | robust | synergy | elevate | empower | transformative
Banned patterns: "Welcome to" | "Discover [product]" | vague aspirational headlines | hedging ("may help you," "can potentially") | generic superlatives
Banned design tells: Inter as sole font | purple-blue gradients | uniform 16px border-radius everywhere | centered everything | Hero/Lucide as sole icon set | abstract 3D blobs | uniform fade-in on all elements | plastic AI stock photos
Fix: ask "Would the founder actually say this?" No → rewrite.
Copy System
- Headlines — benefit-first, specific, numbers, max 8 words
- Subheadings — expand promise, 15-25 words
- Body — one idea/paragraph, 2-4 sentences, active, concrete, benefit-oriented
- CTAs — specific action verb first, gradient primary + ghost secondary, above fold + page end
- Never: "Click here" | "Submit" | "Learn more"
- Personality mapping — Expert → precise industry terms | Direct → short declarative | Pragmatic → outcomes/implementation
- Vocabulary: always-use / never-use / prefer-over lists | max sentence: 25 words
- Banned structures: passive voice, hedging, em dashes mid-sentence | monthly drift audit
Microcopy System
- Error messages — [What happened] + [What to do]. Empathetic, solution-oriented. "Payment failed. Try a different card or contact support." Never "Error 500" or jargon. Flesch 70+.
- CTAs — action verb first, max 3 words preferred: "Start building" | "Ship today" | "Get access"
- Empty states — acknowledge absence + suggest action: "No projects yet. Create your first one." Never just "No results"
- Toasts — past tense success ("Project created"), present in-progress ("Saving…"), plain-language errors
- Form labels — noun phrases, sentence case. Helper text: one line, 10 words max
Machine-Readable Brand Documentation
- DESIGN.md (6 sections): 1. Visual Theme | 2. Color Palette (hex + role) | 3. Typography (family + scale + weight) | 4. Spacing + Layout | 5. Components (states + variants + props) | 6. Elevation + Shadows
- Tokens: W3C DTCG JSON format, single source of truth. Generated from
tokens.json→ CSS vars + Tailwind config.
SEO + Structured Data
Per-page MUST have
- Title 50-60 chars keyphrase-first | meta desc 120-156 chars | canonical
- OG image 1200×630 branded card | one H1 in prerendered HTML
- JSON-LD: WebPage floor; Org/BreadcrumbList/FAQPage/Person/Product/Service only when real entities
pSEO 5 page types
- Integration (
/integrations/{tool}) | Comparison (/compare/{a}-vs-{b}) | Use-case (/for/{audience}) | Template (/templates/{type}) | Location (/{city}-{service}) - Each: unique H1 + meta desc + 800+ unique words + 1 unique image + 3+ internal links + 1+ outbound citation. Never templatize verbatim.
GEO / AI search
- Quotable answer blocks 40-60 words (LLMs cite)
- FAQPage schema highest AI-citation rate
- JSON-LD facts MUST also appear as visible HTML text | lead paragraphs answer query in <40 words
- EEAT: author bio + Person schema +
sameAs+ dated revision + ownership
Trust Surfaces
- Real testimonials w/ verifiable attribution | license/accreditation badges
- Security.txt + privacy + terms | Person JSON-LD w/ sameAs | About + Team pages with real bios
Legal Pages (required)
- /privacy | /terms | /accessibility
See submodules: email-templates, social-automation, seo-and-keywords, documentation-and-codebase-hygiene, per-route-metadata, grammar-audit, build-breaking-rules.
Files (claude-skills)
-
ai-search-geo.md 4 KB
# AI Search / GEO — 2026 Practice ## Bots to Address Explicitly `robots.txt` must take a position on every AI crawler. Default = unmanaged risk. ``` User-agent: GPTBot Allow: / User-agent: Claude-User Allow: / User-agent: Claude-SearchBot Allow: / User-agent: ClaudeBot Disallow: /train Allow: / User-agent: PerplexityBot Allow: / User-agent: Google-Extended Allow: / User-agent: CCBot Allow: / User-agent: Bytespider Disallow: / ``` ### Three Anthropic bots exist — address each separately - **ClaudeBot** — training (most blockable) - **Claude-User** — user-initiated fetches (block at your peril; this is real users) - **Claude-SearchBot** — search index population ### Top blocked agents by traffic (Cloudflare Radar via Calvano, 2025) - GPTBot — 5.20% - CCBot — 5.14% - ClaudeBot — 4.59% - Bytespider — 4.41% - Google-Extended — 4.23% ## What AI Crawlers Actually See - **ChatGPT / Claude on direct fetch** — do NOT parse JSON-LD. They see rendered HTML body text. - **Google AI Overviews / Bing Copilot** — DO index JSON-LD via their existing crawl pipeline. - **Perplexity** — pulls from indexed snippets; JSON-LD helps surface in those snippets. **Conclusion:** keep JSON-LD (it boosts the SERP → AI pipeline), but the on-page HTML body MUST also contain the same structured facts as visible text + readable headings. ## Page Structure for AI Citation 1. **Lead paragraph answers the query in <40 words.** AI chatbots quote the first quotable paragraph. 2. **40-60 word quotable blocks** below each H2 — these are the citation magnets. 3. **5+ JSON-LD blocks per route** — WebSite + Organization + WebPage + BreadcrumbList + **FAQPage** (minimum). Add LocalBusiness / Product / BlogPosting / HowTo / Person by page type. 4. **Author schema** (`Person` with `sameAs`) + `dateModified` on every content page (EEAT). 5. **Claim citations** — `citation: CreativeWork[]` array on Article / BlogPosting / FAQPage JSON-LD. 6. **Concise H1** matches the user query. 7. **Internal links** to related pages (chains pages for AI to "follow up" topics). ## FAQPage Schema (Highest AI Citation Rate) Mandate on every content / product / service page. Minimum 5 Q&A blocks. Each `answerCount` ≥1. ```jsonld { "@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [ { "@type": "Question", "name": "What is X?", "acceptedAnswer": { "@type": "Answer", "text": "<40-60 word direct answer>" } } ] } ``` ## llms.txt **Optional.** <0.3% adoption among top 1000 sites (Rankability, 2025). Server logs show major LLM crawlers (GPTBot, ClaudeBot, PerplexityBot) do NOT request it. Keep `llms.txt` for dev-tool DX (Cursor, Claude Code love it), but NOT a build-breaking gate. If shipping `llms.txt` — `/llms.txt` summary index + `/llms-full.txt` long-form content. ## EEAT 2026 December 2025 Google core update rewarded sites with visible expertise. Required signals: - **Author bio** with credentials + photo + `sameAs` links to LinkedIn / X / scholar profiles - **Person schema** in JSON-LD with `jobTitle`, `worksFor`, `alumniOf`, `sameAs` - **dateModified** prominent on every article (not just JSON-LD) - **Ownership statement** (publisher / organization, address, contact) in footer + Organization schema - **Citations** to peer-reviewed sources or primary data, not other AI summaries ## Quantitative Claims (Anti-Slop) Every %, $, ratio, comparison cites APA 7th inline `(Author, Year)` + reference list entry. Build gate greps `\d+%|\$\d+[MBK]|\d+x` for missing cites. See `rules/citations.md`. ## Common Mistakes - JSON-LD describing facts that aren't in the visible HTML — LLMs don't trust schemas they can't verify - Generic FAQs ("What is your refund policy?") instead of topic-specific. Use queries real users ask. - Author "Admin" or no author at all — destroys EEAT - Stuffing all 5+ JSON-LD blocks into one giant `@graph` — Google handles it, but some validators reject - Forgetting `inLanguage` + `isAccessibleForFree:true` on public content -
build-breaking-rules.md 20.9 KB
--- name: "09 build-breaking brand+content rules" description: "Universal brand/content gates: social brand-hex, small-text WCAG AAA contrast, logo-vs-container contrast, hero scrim, transparent-bg logo variant, X-not-Twitter, institution-credentials row. Migrated verbatim from rules/always.md 2026-05-03." metadata: version: "1.0.0" updated: "2026-05-03" effort: "high" context: "fork" license: "Rutgers" compatibility: claude-code: ">=2.0.0" agentskills: ">=1.0.0" --- # 09 — Build-Breaking Brand + Content Rules > **Model migration note (pass-79, 2026-06-09)**: `DALL-E` → **GPT Image 1.5** + `GPT-4o` → **GPT Image 2 vision**. Per `platform.openai.com/docs/deprecations`. Migrated from `~/.claude/rules/always.md` 2026-05-03. ## Every social link Hover / focus / active swaps to official brand hex: - **FB** — `#1877F2` - **LinkedIn** — `#0A66C2` - **X** — `#000` - **Instagram** — gradient `#F58529 → #DD2A7B → #8134AF → #515BD4` - **YT** — `#FF0000` - **TikTok** — `#000` with cyan / magenta dual-shadow - **Pinterest** — `#BD081C` - **GitHub** — `#181717` - **Discord** — `#5865F2` - **Bluesky** — `#0085FF` - **Threads** — `#000` - **WhatsApp** — `#25D366` - **Reddit** — `#FF4500` - **Snap** — `#FFFC00` Encode as `social-brand-hex.json` shipped in template — never hardcode generic accent. See `social-brand-hex.md`. ## Every site (small text contrast — ***WCAG AAA FOR SMALL TEXT***) - Text ≤14px MUST contrast ≥7:1 vs background (WCAG AAA). - Body small muted text: minimum WCAG-AA (≥4.5:1). - **Forbidden**: muted-foreground on muted-background for small text. - **Build gate**: visual-qa samples computed-style of `font-size ≤ 14px` + bg, fails if contrast <7:1. **Reference incident**: /services small-print on lone-mountain-global-3 (2026-05-01). ## Every logo render (***LOGO-VS-CONTAINER CONTRAST — UNIVERSAL — BUILD-BREAKING***) - Every logo render (header, footer, hero, modal, splash, mobile menu, sidebar) MUST contrast its container background ≥4.5:1 measured on the logo's dominant chroma (NOT transparent pixels). ### Forbidden pairings - White-text-logo on white/cream bg - Dark-text-logo on dark/navy bg - Low-saturation-logo on same-hue bg ### Resolution - Header AND footer themes chosen AFTER logo luminance scan (skill 09 `logo-luminance-drives-theme`). - Dual-theme site: ship TWO logo files (`brand-mark-light.svg` for dark bg, `brand-mark-dark.svg` for light bg) with CSS `<picture>/<source media>` swaps. ### Validator (`validate-logo-contrast.mjs`) GPT Image 2 vision samples logo bbox + container computed bg at 6bp, fails if contrast <4.5:1. **Reference incident**: lone-mountain-global-3 (2026-05-01) white-text-logo on white bg AND dark-text-logo on dark bg — both invisible. ## Every hero (***HERO TEXT CONTRAST SCRIM — UNIVERSAL — BUILD-BREAKING***) Hero / page-banner backgrounds MUST guarantee ≥4.5:1 contrast for ALL hero text via mandatory scrim. ### Pattern ```css .hero::before { content:""; position:absolute; inset:0; background: linear-gradient(180deg, rgba(0,0,0,.45) 0%, rgba(0,0,0,.65) 100%); z-index:1 } .hero > .hero-content { position:relative; z-index:2 } ``` For dark text on light bg, invert to white-overlay. ### Scrim opacity tuned per bg luminance - Bright bg — 55-70% scrim - Mid bg — 35-50% scrim - Dark bg — 25-35% scrim ### Companion - `text-shadow: 0 1px 3px rgba(0,0,0,.5)` on hero h1 + subhead as belt-and-suspenders. - NEVER ship hero text on raw image without scrim. ### Validator visual-qa samples hero text + computed-bg-after-scrim at 6bp, fails if contrast <4.5:1. **Reference incident**: lone-mountain-global-3 (2026-05-01) hero text on insufficiently-darkened bg. ## Every nav / header / footer logo render (***LOGO TRANSPARENT-BG VARIANT — UNIVERSAL — BUILD-BREAKING***) When source logo PNG/JPG has a baked-in solid background AND the rebuild surface DIFFERS from that baked bg, produce a transparent-bg logo variant before render. ### Pipeline 1. Detect baked bg via Sharp `getDominantColor` + edge-pixel sample at 5px inset — ≥80% border pixels sharing one hue = baked bg. 2. Auto-strip via `sharp(logo).removeAlpha().threshold(...)` OR remove.bg API (`REMOVEBG_API_KEY`) for hard cases. 3. Ship `brand-mark-transparent.png` + `brand-mark-transparent.svg` alongside `brand-mark.png`. 4. `<picture><source srcset="brand-mark-transparent.png" media="(--surface-bg: transparent)"><img src="brand-mark.png"></picture>` with CSS `--surface-bg` per surface. ### Context rules - Header on white nav → transparent variant - Header on dark hero → dark-bg variant - Footer on dark band → light-text variant ### Validator (`validate-logo-transparent-variant.mjs`) For every site with detected baked bg: assert `public/brand-mark-transparent.png` exists AND header `<img>` references transparent variant when nav bg-luminance differs from logo baked-bg luminance by ≥0.3. **Reference incident**: njsk.org rebuild (2026-05-02) white-card-bg logo on white nav. ## Every X (formerly Twitter) reference (***X-NOT-TWITTER + LATEST X ICON — UNIVERSAL — BUILD-BREAKING***) Every mention / icon / aria-label / alt-text referencing the network formerly known as Twitter MUST use "X" branding (text="X", icon=current X logo SVG, brand color=`#000` with cyan/magenta dual-shadow). ### Forbidden text in dist/ HTML + ARIA - "Twitter" (except "formerly Twitter" historical context) - "Tweet" (except as past-tense in dated content) ### Forbidden icons - Twitter bird SVG; `simple-icons:twitter`; `lucide-twitter` ### Required - `simple-icons:x` OR custom inline: ```html <svg viewBox="0 0 24 24"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg> ``` - `aria-label` MUST be "X (formerly Twitter)" on first mention, plain "X" on repeat. ### Validator (`validate-x-not-twitter.mjs`) Grep dist/ for `\bTwitter\b`, `tweet`, `tw-bird`, blue twitter-bird SVG path; fail outside whitelisted contexts. **Reference incident**: nyfoldingbox.com (2026-05-02) shipped Twitter-bird icon + "Follow us on Twitter." ## Every bio mentioning notable institutions BU | Harvard | MIT | Stanford | Oxford | Cambridge | Yale | Princeton | Columbia | UPenn | Cornell | Brown | Dartmouth | Caltech | UChicago | Northwestern | Duke | JHU | UMich | UCLA | UCB | CMU | T20 — credentials row with high-res transparent-PNG institution logos. **Fetch chain**: Logo.dev → Wikipedia Commons SVG → Brandfetch. ## Every section with multiple CTAs (***PRIMARY vs SECONDARY VISUAL DISTINCTION — UNIVERSAL — BUILD-BREAKING***) Each section: AT MOST one primary CTA (filled, high-contrast, brand-accent bg, white text) + optionally one secondary CTA (outlined/ghost). NEVER two primary CTAs in the same visual space. ### Design tokens - **Primary CTA** — `background: var(--brand-accent); color: var(--bg-primary); border: none; font-weight: 700; padding: .75rem 1.75rem; border-radius: var(--radius-btn, 6px)` - **Secondary CTA** — `background: transparent; color: var(--brand-accent); border: 1.5px solid var(--brand-accent); font-weight: 600` ### Rules - Hover states required on both (per 4-state interactive rule). - Icon `→` or `↗` on primary; no icon on secondary. - **Mobile stacking**: flex row wraps to column on `<576px`; primary always first (top). - **Hero exception**: video/tour secondary may be `<button role="button" aria-label="Watch overview video">▶ Watch 2-min overview</button>` ghost-styled. ### Validator (`validate-cta-hierarchy.mjs`) Per `<section>`: count `[class*="btn-primary"]`, `[data-variant="primary"]` — assert count ≤1; assert second CTA has distinct visual weight. **Reference incident**: nyfoldingbox — every section had two identically-styled "GET STARTED" + "LEARN MORE" buttons. ## Every testimonial (***COMPLETE ATTRIBUTION — UNIVERSAL — BUILD-BREAKING***) Every testimonial MUST include: 1. `<blockquote>` with `cite` attribute (source URL when available). 2. `<cite>`: person first + last name (MANDATORY), job title/role (MANDATORY), company/org (MANDATORY when business context exists). 3. **Avatar**: source photo ≥40×40px → GPT Image 1.5 headshot → initials monogram (bg=brand-accent, text=bg-primary). 4. **Date** (year minimum: "2024") when derivable. 5. **Star rating** (1-5) when source had one. ### NEVER anonymous testimonials - `"— Happy Customer"` — FORBIDDEN - `"— Anonymous"` — FORBIDDEN - `"— Satisfied Client"` — FORBIDDEN - Fallback: `"[Name withheld by request] · Verified customer · 2024"`. ### Validator (`validate-testimonial-attribution.mjs`) Every `<blockquote>` must contain `<cite>` with length >10 AND cite text must NOT match `/(anonymous|happy customer|satisfied client|a customer)/i`. **Reference incident**: nyfoldingbox source had partial attribution ("— Mike, NJ") — rebuild stripped even that. ## Every external link in body content (***OPENS NEW TAB + EXTERNAL INDICATOR — UNIVERSAL — BUILD-BREAKING***) Every `<a href>` pointing to an external domain in body content MUST: 1. `target="_blank"` to preserve reading context. 2. `rel="noopener noreferrer"` (security + privacy). 3. External-link indicator AFTER link text — `<span aria-hidden="true" class="ext-link-icon">↗</span>` 12×12. 4. `aria-label="[link text] (opens in new tab)"` on the `<a>`. **Exception**: nav links to known sister sites (same org) may open in same tab. ### Validator (`validate-external-links.mjs`) Every `<a>` with external href in content area has `target="_blank"` AND `rel` contains `noopener` AND sibling `<span class="ext-link-icon">` exists. **Reference incident**: both sites linked publications in body text that opened same tab. ## Every Schema.org `@type` selection (***BUSINESS-CLASS GATING — UNIVERSAL — BUILD-BREAKING***) `LocalBusiness` (and subtypes) MUST be reserved for businesses where customers physically visit a fixed-address premises. ### Allowlist for `@type=LocalBusiness` Storefront retail | Restaurant/cafe/bar | Medical/dental/vet clinic | Salon/spa/barbershop | Gym/yoga studio | Auto repair/dealership | Hotel/lodging | Attorneys/CPAs with walk-in | Real estate brokerage with walk-in. Everything else MUST use `@type=Organization` (or subtypes: NGO, Corporation, EducationalOrganization, GovernmentOrganization). ### Implementation - Classify `_research.json.business_class` ∈ `storefront | restaurant | medical | salon | gym | auto-repair | hotel | legal-walkin | real-estate-walkin | organization | nonprofit | saas | portfolio | agency | research | foundation` BEFORE schema render. - `buildBusinessJsonLd({ businessClass })` in `src/lib/businessSchema.ts` emits correct `@type` — never inline `@type: 'LocalBusiness'`. ### Validator (`validate-schema-type.mjs`) Grep dist HTML for `"@type":"LocalBusiness"` — assert `_research.json.business_class` is in local-business allowlist; else fail. **Reference incident**: lonemountainglobal (2026-05-07) global health consulting practice rendered as `LocalBusiness` with no street address. ## Every site with named founder / principal (***PERSON JSON-LD — UNIVERSAL — BUILD-BREAKING***) Every site naming an individual as founder/principal/lead/director/author on homepage or `/about` MUST emit `Person` JSON-LD on that page: - `name` (MANDATORY), `jobTitle` (MANDATORY), `worksFor: { '@type': 'Organization', name }` (MANDATORY when org named) - `url`, `image`, `alumniOf`, `sameAs` (LinkedIn, ORCID, Google Scholar, Wikipedia, X, GitHub, Mastodon), `description`, `knowsAbout` Use `<PersonJsonLd>` component — never hand-write Person JSON-LD inline. ### Validator (`validate-person-jsonld.mjs`) For every dist page rendering `<h1>`/`<h2>` containing a person's name + job title within 100 characters: assert ≥1 `<script type="application/ld+json">` block of `@type=Person` with matching `name`. Missing = fail. **Reference incident**: lonemountainglobal — founder named as global health expert with 30+ years, zero Person JSON-LD emitted. ## Every publication / article / news render (***ARTICLE JSON-LD — UNIVERSAL — BUILD-BREAKING***) Every publication, article, blog post, news item, white paper MUST emit one of: - `@type=ScholarlyArticle` (peer-reviewed) | `@type=BlogPosting` | `@type=NewsArticle` | `@type=Article` With: `headline`, `url`, `datePublished` (ISO 8601), `dateModified`, `author: [{ @type: Person, name, url? }]`, `publisher: { @type: Organization, name, logo? }`, `mainEntityOfPage`, `inLanguage`, `keywords`, `citation: [{ @type: CreativeWork, name, url }]`. Use `<ArticleJsonLd>` component covering all four type variants. ### Validator (`validate-article-jsonld.mjs`) Every route matching `/blog/*`, `/publications/*`, `/news/*`, `/research/*`, `/articles/*`, `/papers/*`, `/case-studies/*` MUST emit one of the four article JSON-LD types; missing `author` + `datePublished` + `publisher` = fail. **Reference**: lonemountainglobal listed publications inline on /about with no per-publication JSON-LD. ## Every page on a research-bearing site (***STYLIZED APA REFERENCES — UNIVERSAL — BUILD-BREAKING***) ≥80% of routes MUST surface 3-10 APA-7th-ed references via `<PageReferences refs={...} />` AND ≥1 inline `<Citation refId="...">` per section making a quantitative/factual claim. ### Pipeline per page 1. Before rewriting copy, spawn a research agent mining 5-10 APA-cited claims from peer-reviewed/.gov/.edu/primary sources. 2. Write `src/data/citations/<slug>.ts` exporting `{ id, apa, url, type }`. 3. Inject `<Citation refId>` markers around each quantitative claim. 4. Render `<PageReferences slug="<slug>" />` above footer. ### Exemptions (allowed to skip) Pure transactional routes (`/checkout`, `/cart`, `/login`, `/signup`), interactive tools (`/contact`, `/admin`), pure-listing pages whose children carry citations. ### Page-level minimums - Home — 3+ refs - About — 8+ refs - Services — 5+ refs - Donate — 5+ refs - Financials/annual-report — 5+ refs - Press — 3+ refs - FAQ — 5+ refs ### Stylized rendering REQUIRED — never a bare `<ol>` of plain text - Monospace refId chip (e.g. `[smith24]` in `var(--font-mono)`) - APA-correct hanging indent (4ch) - Italic journal/book titles - Hover expands to 1-line abstract - Copy-link button per ref (`navigator.clipboard.writeText(apa)`) - Accordion collapse on mobile; IntersectionObserver fade-in Component: `src/components/page-references.tsx` (shipped in template). ### Validator (`validate-stylized-citations.mjs`) For every non-exempt route: assert ≥3 entries in `src/data/citations/<slug>.ts` AND ≥1 `<Citation` JSX call AND `<PageReferences` rendered. **Reference incident**: njsk.org first audit (2026-05-11) — 14 pages with unsourced quantitative claims ("97 cents on the dollar", "23.4% poverty rate", "1 in 4 children"). **Companion**: `rules/citations.md` + skill 03-planning-and-research. ## Every site with analytics (***COOKIE CONSENT BANNER — UNIVERSAL — BUILD-BREAKING — GDPR + CCPA***) Every site loading third-party analytics MUST render a cookie consent banner on first visit: 1. Bottom-bar or bottom-corner modal — NEVER full-screen block. 2. Minimum message: "We use analytics to improve your experience. [Accept All] [Manage Preferences] [Reject Non-Essential]" — all three options visible. 3. "Accept All" → `localStorage.setItem('cookie-consent', 'all')` + initialize analytics. 4. "Reject Non-Essential" → `localStorage.setItem('cookie-consent', 'essential-only')` + block all analytics (NEVER load GA4/PostHog before consent in EU). 5. Banner NEVER auto-dismissed; NEVER shown again after a choice is stored. 6. "Manage Preferences" opens modal with per-category toggles (Analytics, Marketing, Functional). `src/lib/consent.ts` template module checks localStorage on load, conditionally initializes analytics. ### Validator (`validate-cookie-consent.mjs`) Playwright clears localStorage, loads homepage, asserts `[data-cookie-banner]` visible within 2s AND has ≥2 interactive choice buttons. **Reference incident**: both sites loaded PostHog + GTM before any consent — GDPR violation. ## Every site rebuild (***BRAND FIDELITY #1 — LOGO PIXEL-MATCH VALIDATOR — UNIVERSAL — BUILD-BREAKING***) Source-site logo extraction MUST produce an asset whose perceptual hash (pHash) differs ≤8 from the original. ### Validator (`validate-logo-fidelity.mjs`) Compute pHash of `_brand.json.logo.source_url` vs dist `assets/brand/logo.*`, fail if Hamming distance >8. Regenerate via Ideogram v3 ONLY when source is unrecoverable (404, redirect-loop, <64px); record `_brand.json.warnings: ["logo_regenerated_no_source"]`. **Reference**: rec #17 (2026-05-10) — LMG iter-4 shipped Ideogram-regenerated logo while source SVG was 200-OK. ## Every site rebuild (***BRAND FIDELITY #2 — FONT-FAMILY EXTRACTION — UNIVERSAL — BUILD-BREAKING***) Playwright MUST sniff source-site computed-style (`getComputedStyle(h1)` + `body`) — NOT inferred from category or guessed. Write `_brand.json.fonts: { headline, body, mono?, sources: [google|adobe|self-hosted] }`. ### Validator (`validate-font-fidelity.mjs`) Grep dist `<link href="https://fonts.googleapis.com/css2?family=...">` against `_brand.json.fonts.{headline,body}`, fail on missing or mismatch. **Reference**: rec #18 (2026-05-10) — LMG iter-4 source uses Poppins+Hind, pipeline shipped Inter+Roboto. ## Every site rebuild (***BRAND FIDELITY #3 — COLOR EXTRACTION — UNIVERSAL — BUILD-BREAKING***) Run TWO passes: 1. GPT Image 2 vision on source homepage screenshot → `primary, secondary, accent, bg_dark, bg_light` hex array. 2. `node-vibrant`/`Vibrant.js` on extracted logo PNG → palette. **Merge**: vision-pass wins for primary/accent; vibrant wins for backgrounds. Write to `_brand.json.colors.{primary,secondary,accent,bg,fg,muted}`. ### Validator (`validate-brand-colors.mjs`) Assert `_brand.json.colors.primary` differs ≤ΔE 5 from source-site primary (CIE Lab) AND no color is hand-guessed. **Reference**: rec #19 (2026-05-10) — njsk.org burgundy incident (LLM guessed `#1e3a8a` blue, actual `#6b1d2e` burgundy). ## Every site rebuild (***BRAND FIDELITY #4 — CONTENT CORPUS DELTA — UNIVERSAL — BUILD-BREAKING***) Source content corpus MUST be preserved with delta ≤15% — Jaccard similarity of token-bag per route. Build cannot rewrite >15% of source paragraphs except (a) banned-word substitution per copy-writing rules, (b) explicit user-requested rewrite. ### Validator (`validate-content-fidelity.mjs`) Per rebuilt route, compute Jaccard against `_content_corpus.json[path]`, fail if <0.85. **Reference**: rec #20 (2026-05-10) — LMG iter-4 rewrote 40% of "About" page, lost authentic founder language. ## Every site rebuild (***BRAND FIDELITY #5 — INTERNAL ROUTE STRUCTURE PARITY — UNIVERSAL — BUILD-BREAKING***) Every source URL becomes a real rendered route OR a 301 in `_redirects`. Deep-crawl source via sitemap.xml + recursive link discovery → write `_routes.json.{ source_path, rebuild_path, status: 200|301, redirect_target? }`. ### Validator (`validate-route-parity.mjs`) Every `_routes.json` entry resolves to a 200-OK rebuild HTML file OR appears in `_redirects` with target also 200-OK. **Reference**: rec #21 (2026-05-10) — broken links from missing routes = SEO equity loss. ## Every site rebuild (***BRAND FIDELITY #6 — TONE-OF-VOICE VECTOR MATCH — UNIVERSAL — BUILD-BREAKING***) Source copy MUST be vectorized (OpenAI `text-embedding-3-small`) per page; rebuild copy embedding MUST match source within cosine ≥0.78. Source crawl → embed body text per route → `_voice_embeddings.json`. After rebuild: embed rebuild body text → compare. ### Validator (`validate-voice-fidelity.mjs`) Fail any route with cosine <0.78 against its source counterpart. **Reference**: rec #22 (2026-05-10) — LMG iter-4 rebuild reads like SaaS landing page, source reads like founder essay. ## Every site rebuild (***BRAND FIDELITY #7 — HERO IMAGE/VIDEO SOURCE-FIRST PRIORITY — UNIVERSAL — BUILD-BREAKING***) Rebuild hero MUST attempt source-site reuse FIRST — fall back to Sora/GPT Image 1.5/Pexels only when source is unrecoverable. Source hero: R2 self-host → `<picture>`/`<video>` slot. Upgrades quality (upscale, sharpen, recompress) but does NOT replace subject. ### Validator (`validate-hero-source-first.mjs`) If `_media_extraction.json.hero_url` resolves 200, assert rebuild hero file is derived from same source (matching pHash or from same URL). **Reference**: rec #23 (2026-05-10) — Mission Doctrine cinematic floor + brand fidelity together. ## Every site rebuild (***BRAND FIDELITY #8 — DESIGN-TOKEN DIFF ARTIFACT — UNIVERSAL — BUILD-BREAKING***) Every rebuild MUST emit `_brand_diff.json`: per-token comparison source-extracted vs rebuild-shipped (logo pHash, font family, color hex, typescale, spacing scale, radius). ### Validator (`validate-brand-diff.mjs`) Assert `_brand_diff.json` exists with fields `{logo_hash_distance, font_match, color_delta_e_max, token_delta_count}` AND a human-readable summary line per token. **Reference**: rec #24 (2026-05-10) — auditability prevents silent regression. -
documentation-and-codebase-hygiene.md 3.1 KB
--- name: "Documentation and Codebase Hygiene" version: "1.1.0" updated: "2026-04-23" description: "Keep codebase in sync: README (install.doctor template, shields.io badges), CLAUDE.md, MEMORY.md, JSDoc/TypeDoc, stale code removal. Stale docs = bugs. Runs continuously." --- # Documentation and Codebase Hygiene Stale docs are bugs. The entire codebase (code, comments, markdown, configs) stays in sync at all times. ## README.md (install.doctor Template) ```markdown <div align="center"> <img src=".config/assets/logo.png" width="148" height="148" /> <h1>Project Name</h1> <p><em>One-line description</em></p> </div> <!-- Badges (for-the-badge style) --> []() []() []() <!-- Dividers between sections --> <img src="https://gitlab.com/megabyte-labs/assets/-/raw/master/png/aqua-divider.png" width="100%" /> ## Overview | ## Features | ## Quick Start | ## Tech Stack | ## Documentation | ## License ``` ## CLAUDE.md Standards - **Required sections:** What This Is, Tech Stack, Project Structure, Key Patterns, Commands, Current State - Keep <200 lines - Update after every session ## Code Comments ### When - WHY (not what) - Complex algorithms - Workarounds with issue links - API contracts - Security decisions ### Never - Restating code - TODO without issue link - Commented-out code (git has history) ```typescript // GOOD: Clerk webhook sends display_name, but our DB schema uses name user.name = clerkUser.display_name; // GOOD: Rate limit: 10 req/60s per IP (CF KV-based). See: https://... ``` ## JSDoc Every exported function — `@param`, `@returns`, `@example`, `@see` (with URL). Descriptions <2 sentences. ## Stale Code Removal ### Remove - Commented-out code - Unused imports - Dead functions - TODO >30 days without issue - `console.log` debug - Old applied migrations ### Keep - User-written code - Documented workarounds with links - Active feature flags **Rule:** Never remove user code without asking. Use `npx knip` for unused exports. ## Sync Protocol - **Every session** — README matches reality, CLAUDE.md current, remove deleted file refs - **Feature completion** — update README features, CLAUDE.md patterns, relevant skills - **Refactor** — update all docs, rename refs, grep for old names, update project structure ## Naming Conventions - **Files** — kebab-case - **Functions** — camelCase - **Types** — PascalCase - **Constants** — SCREAMING_SNAKE - **DB columns** — snake_case ## Quality Checks ```bash grep -rn "TODO\|FIXME" src/ --include="*.ts" | head -20 grep -rn "console\.log" src/ --include="*.ts" | grep -v "// keep" npx knip --reporter compact ``` ## Ownership - **Owns:** README template, CLAUDE.md standards, code comment quality, JSDoc, stale code removal, cross-project sync, AI-readable formatting - **Never owns:** Content writing (→09, 22), implementation (→06), testing (→07), deployment (→08) -
email-templates.md 6 KB
--- name: "Email Templates" version: "1.1.0" updated: "2026-04-23" description: "Branded HTML transactional email: dark gradient header, clean body, dark mode. Contact confirmations, newsletter welcome, donation receipts. Resend via verified megabyte.space." --- # Email Templates ## Design Principles (Source: Litmus, Really Good Emails) - **Dark mode support** — emails must look great in both light and dark email clients - **Max width: 560px** — optimal reading width across all clients - **System fonts** — `-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif` - **Single-column** — simplest, most reliable across email clients - **Minimal images** — email clients often block images by default - **Text-first** — key information must be readable without images loading ## Template Structure ``` ┌─────────────────────────────┐ │ Dark gradient header (#060610 → #0a0a1a) │ │ Logo (center, max 120px wide) │ ├─────────────────────────────┤ │ White body (padding: 32px) │ │ Content (16px, line-height: 1.6) │ │ CTA button (brand gradient, centered) │ ├─────────────────────────────┤ │ Light footer (#f5f5f5) │ │ Unsubscribe link + address │ └─────────────────────────────┘ ``` ## Base HTML Template ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="color-scheme" content="light dark"> <meta name="supported-color-schemes" content="light dark"> <title>Email Subject</title> <style> :root { color-scheme: light dark; } body { margin: 0; padding: 0; background: #f0f0f5; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } .wrapper { max-width: 560px; margin: 0 auto; } .header { background: linear-gradient(135deg, #060610, #0a0a1a); padding: 24px; text-align: center; border-radius: 12px 12px 0 0; } .header img { max-width: 120px; height: auto; } .body { background: #ffffff; padding: 32px; font-size: 16px; line-height: 1.6; color: #333333; } .body h1 { font-size: 22px; color: #060610; margin-top: 0; } .cta { display: inline-block; background: linear-gradient(135deg, #00E5FF, #50AAE3); color: #060610; font-weight: 600; padding: 14px 32px; border-radius: 8px; text-decoration: none; margin: 16px 0; } .divider { border: none; border-top: 1px solid #50AAE3; margin: 24px 0; opacity: 0.3; } .footer { background: #f5f5f5; padding: 20px 32px; font-size: 12px; color: #999999; text-align: center; border-radius: 0 0 12px 12px; } .footer a { color: #50AAE3; } /* Dark mode */ @media (prefers-color-scheme: dark) { body { background: #1a1a2e !important; } .body { background: #0f0f1f !important; color: #e0e0e5 !important; } .body h1 { color: #f0f0f5 !important; } .footer { background: #060610 !important; color: #666680 !important; } } </style> </head> <body> <div class="wrapper"> <div class="header"> <img src="https://domain.com/logo.png" alt="Brand Name"> </div> <div class="body"> <!-- Content here --> </div> <div class="footer"> <p>Brand Name — brian@megabyte.space</p> <p><a href="{{{unsubscribe}}}">Unsubscribe</a> · <a href="https://domain.com/privacy">Privacy</a></p> </div> </div> </body> </html> ``` ## Email Types ### 1. Contact Form Confirmation **Subject:** "Got it — we'll be in touch" ``` Hi {{name}}, Thanks for reaching out. We received your message and will get back to you shortly. Here's what you sent: — Subject: {{subject}} — Message: {{message}} If this is urgent, reply directly to this email. Best, Brian ``` ### 2. Newsletter Welcome **Subject:** "You're in 🎉" ``` Welcome to the [Brand] newsletter. Here's what to expect: - Updates on our mission and impact - New features and improvements - Stories from the community No spam. Unsubscribe anytime. [CTA: Visit Our Site] ``` ### 3. Donation Receipt **Subject:** "Thank you — here's your receipt" ``` Thank you for your generous donation of ${{amount}}. Date: {{date}} Transaction ID: {{transaction_id}} Tax-deductible: This receipt may be used for tax purposes. Your support directly funds [specific impact]. [CTA: Share with Friends] ``` ### 4. Volunteer Signup **Subject:** "Welcome aboard" ``` You're officially signed up to volunteer. Next steps: 1. We'll reach out within 48 hours with scheduling 2. Bring: comfortable clothes and a willingness to help 3. Location: [address] Questions? Reply to this email. ``` ### 5. Goal Reached Celebration **Subject:** "We did it! Goal reached." ``` Thanks to people like you, we've reached our ${{goal}} goal! {{raised}} raised from {{donor_count}} supporters. This means [specific impact statement]. [CTA: See the Impact] ``` ## Sending via Resend ```typescript import { Resend } from 'resend'; const resend = new Resend(env.RESEND_API_KEY); await resend.emails.send({ from: 'Brian <brian@megabyte.space>', to: [recipient], subject: 'Subject line', html: renderedTemplate, reply_to: 'brian@megabyte.space', }); ``` - **Verified domain** — megabyte.space - **API key** — shared key pool (05/shared-api-pool) - **Rate limit** — 100 emails/day on free tier - **For bulk** — use Listmonk on Cloudflare Containers ## Testing Checklist - [ ] Renders correctly in Gmail (web + mobile) - [ ] Renders correctly in Apple Mail - [ ] Dark mode displays properly - [ ] Logo loads (and email is readable without it) - [ ] CTA button is tappable on mobile (min 44px height) - [ ] Unsubscribe link works - [ ] Links are absolute URLs (not relative) - [ ] No broken images - [ ] Subject line <50 characters - [ ] Preview text is meaningful (not "View in browser") -
grammar-audit.md 3.7 KB
--- name: "grammar-audit" description: "Final LLM-powered grammar/clarity pass after copy generation. Fixes typos, agreement, tense, capitalization, Oxford comma WITHOUT rewriting or removing information. Outputs a diff, not a rewrite." updated: "2026-05-01" --- # Grammar Audit (***FINAL COPY PASS — EVERY SITE, EVERY BUILD***) After all copy is generated (hero, body, blog, FAQ, microcopy, legal), run `grammar-audit.mjs` (GPT Image 2 vision-mini, concurrency 5-8) to fix mechanical errors WITHOUT rewriting. The pass is a corrective filter, not a creative rewrite. ## Mandate (NON-NEGOTIABLE) ### Fix - Typos - Spelling - Subject-verb agreement - Tense consistency - Pronoun agreement - Apostrophes / possessives - Hyphens vs en / em dashes - Capitalization (proper nouns, titles, sentence case) - Oxford comma consistency (apply Oxford by default — flip site-wide if voice rejects it) - Quotation mark direction - Spacing (single space after period) - Numbers (≥10 numeric, <10 spelled — except dates / times / money) - a vs an before vowel sounds - than vs then - who vs whom (when natural) - its vs it's - their / there / they're - affect vs effect ### NEVER - Rewrite for style - Shorten - Lengthen - Change meaning - Remove content - Replace banned-word with synonym (anti-slop is skill 09's job, not grammar's) - Rephrase voice - Add / remove information - Reformat structure - Translate idioms ## Output Shape ```json { "edits": [ { "file": "src/data/blog-posts.ts", "before": "She recieved her PHD in 2019,", "after": "She received her PhD in 2019,", "reason": "spelling+capitalization" }, { "file": "src/pages/about.tsx", "before": "...colleagues, mentors and students.", "after": "...colleagues, mentors, and students.", "reason": "oxford-comma" } ] } ``` Apply edits in-place via `Edit` tool calls. Idempotent — re-running on already-corrected copy produces empty `edits[]`. ## Run Conditions 1. After `clean_content` blog import (skill 15) and after `enhance-blog-posts.mjs` 2. After main page copy generation (homepage, about, services, contact, FAQ) 3. After legal page copy (privacy, terms, accessibility, cookies) 4. Before `gorgeous-loop` final pass (skill 06) 5. Re-run on demand via `node scripts/grammar-audit.mjs --only=<file>` ## Concurrency + Cost - GPT Image 2 vision-mini at ~$0.15/1M input + $0.60/1M output - Per site — ~50K input tokens (all copy concatenated) + ~5K output (diff only) = ~$0.01/site - Concurrency 5-8 against the API - CLI — `--only=path`, `--limit=N`, `--dry-run` ## Scope Boundaries (***WHAT IT NEVER TOUCHES***) - Direct quotes (preserve verbatim — author voice) - Named-entity spelling (verified against `_research.json`) - Brand names - Technical jargon (verified against `_research.json.industry_terms`) - Code blocks - URLs - Email addresses - Phone numbers - Dates - Money figures - Proper nouns flagged in `_research.json.entities[]` ## Hard Gate After grammar pass — re-run `gate` script. If grammar pass introduced any error caught by other gates (link 404, citation regex match, banned-word match), revert that specific edit and flag in `audit_logs`. Grammar pass is corrective — it never breaks other gates. ## Voice Preservation Author voice is sacred. Never normalize "ain't" → "is not" if voice is colloquial. Never remove rhetorical fragments. Never add "that" when omitted intentionally ("She knew [that] he'd come."). The audit asks — "Did the author intend this?" → if yes, leave alone. ## Reference Anchors - Strunk & White (1959) for clarity baseline - Chicago Manual of Style 17th ed for capitalization / comma - APA 7th for academic citations - AP Stylebook for journalism **Default:** Chicago for body copy, AP for blog / news, APA for citations (`rules/citations.md`). -
per-route-metadata.md 6.8 KB
--- name: "per-route-metadata" description: "Static head metadata contract per route. RouteMetadata interface, hydrated <head> template, per-route OG image (Satori-rendered, never hero photo reuse), generous internal linking, publication+external-source linking patterns." updated: "2026-05-01" --- # Per-Route Static Head Metadata (***BUILD-BREAKING — EVERY ROUTE***) Every route emits a fully-hydrated static `<head>` at SSG / prerender time. NEVER client-router-only — crawlers and AI agents read the raw HTML before JS executes. ## RouteMetadata Interface (SSOT — type-checked at build) ```ts interface RouteMetadata { route: string; slug: string; title: string; description: string; canonicalUrl: string; siteName: string; locale: string; type: 'website'|'article'|'profile'|'product'|'service'|'local_business'|'nonprofit'; keywords: string[]; image: { url: string; secureUrl: string; width: 1200; height: 630; alt: string; mimeType: 'image/jpeg'|'image/png'|'image/webp' }; twitter: { card: 'summary_large_image'; title: string; description: string; image: string; imageAlt: string }; jsonLd: Record<string, unknown>[]; robots: { index: boolean; follow: boolean }; } ``` **Source:** `src/data/page-meta.ts` exports `Record<route, RouteMetadata>`. Blog / dynamic routes derive at build via the same factory. **Hard gate:** every `<Route path>` in `App.tsx` has an entry — fail if any route resolves to `index.html`'s static title. ## Per-Route Head Template (paste verbatim — SSOT) ```html <title>{{title}}</title> <meta name="description" content="{{description}}" /> <link rel="canonical" href="{{canonicalUrl}}" /> <meta property="og:title" content="{{title}}" /> <meta property="og:description" content="{{description}}" /> <meta property="og:type" content="{{type}}" /> <meta property="og:url" content="{{canonicalUrl}}" /> <meta property="og:site_name" content="{{siteName}}" /> <meta property="og:locale" content="{{locale}}" /> <meta property="og:image" content="{{image.url}}" /> <meta property="og:image:secure_url" content="{{image.secureUrl}}" /> <meta property="og:image:width" content="1200" /> <meta property="og:image:height" content="630" /> <meta property="og:image:type" content="{{image.mimeType}}" /> <meta property="og:image:alt" content="{{image.alt}}" /> <meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:title" content="{{twitter.title}}" /> <meta name="twitter:description" content="{{twitter.description}}" /> <meta name="twitter:image" content="{{twitter.image}}" /> <meta name="twitter:image:alt" content="{{twitter.imageAlt}}" /> <meta name="robots" content="{{index}}, {{follow}}" /> <script type="application/ld+json">{{jsonLd}}</script> <meta name="theme-color" content="{{brandThemeColor}}" /> <meta name="application-name" content="{{siteName}}" /> <meta name="apple-mobile-web-app-title" content="{{siteName}}" /> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="mobile-web-app-capable" content="yes" /> ``` ## Per-Route OG Image (***1200×630 — DESIGNED CARD, NEVER HERO PHOTO***) - **Generate with Satori preferred** — edge-rendered, deterministic, brand-card layout: title + sitelogo + accent gradient + tagline + brand watermark - **Fallback** — gpt-image-1.5 with brand colors + business name + tagline + logo bottom-right - **NEVER** — reuse homepage hero photo as OG image — must be a designed card - **Store at** `r2://sites/<slug>/og/<route-slug>.jpg` (≤100KB, JPEG q=85, 1200×630) - Each route gets a unique card - **Build gate** — every route's `og:image` URL HEADs 200 + size ≤100KB + dimensions 1200×630 ## Generous Internal Linking (***≥5 PER PAGE — body-copy contextual***) - Every page contains **≥5 internal links woven into body copy** with descriptive anchor text (never "click here", "learn more") - When body mentions: institutions / journals / conferences / awards / organizations / people / places, link them - Internal where a relevant page exists; outbound `rel="noopener" target="_blank"` to authoritative source otherwise (journal name → journal homepage, university → official site, conference → official site) - Anchor text varies — never repeat identical text site-wide - **Hard gate:** `validate-internal-links.mjs` counts contextual `<a>` per page, flags pages with <5 or with repeated identical anchors >2× ## Publication / External-Source Linking (***FOR PORTFOLIO/ACADEMIC/PRESS LISTINGS***) When site lists publications, papers, articles, talks, press, awards, podcasts, each item card MUST contain: 1. Auto-extracted ~40-word summary from the original detail page (crawled at research time) 2. Deep-link to original (`target="_blank" rel="noopener"`) 3. Hyperlinked source name (journal / publisher / conference) → its homepage or DOI ### Rules - NEVER import direct quoted abstracts (SEO duplicate-content risk + copyright) - Summary = our paraphrase - Date + author + type + ISBN (if any) extracted as structured metadata (BlogPosting / Article / ScholarlyArticle JSON-LD) ### Build gate Every publication card has summary + outbound link + source-name link OR fail. ## Publication Item Schema ```ts interface PublicationItem { title: string; summary: string; // ~40 words paraphrase, NEVER quoted abstract authors: string[]; date: string; source: { name: string; url: string }; // journal homepage, conference site deepLink: string; // direct link to article/DOI type: 'paper'|'article'|'talk'|'book_chapter'|'podcast'|'press'; isbn?: string; doi?: string; } ``` ## Sub-Page Type Mapping - **homepage** → `website` - **about / team / bio** → `profile` - **service / product detail** → `service` / `product` - **blog post** → `article` - **restaurant / salon / clinic** → `local_business` - **nonprofit** → `nonprofit` ### JSON-LD schema by type - **home** — WebSite + Organization - **bio** — Person + ProfilePage - **service** — Service + Organization - **article** — BlogPosting + BreadcrumbList - **local** — LocalBusiness + PostalAddress + GeoCoordinates - **nonprofit** — NGO + Organization Always + BreadcrumbList on non-home routes. ## Anchor Text Variation (anti-templating) Never reuse identical anchor text across the site. Vary phrasing per occurrence: "her work at Boston University" / "Boston University doctoral program" / "BU's nutrition department". Each occurrence reads naturally in context. ## Validator: validate-route-metadata.mjs For every route in `sitemap.xml`: - Fetch raw HTML (NOT executed JS) - Assert: - Title 50-60ch - Desc 120-156ch - Canonical present - `og:image` + `og:title` + `og:description` present non-empty - `twitter:card=summary_large_image` - JSON-LD parseable + ≥4 blocks - h1 in raw HTML before any `<script>` - Fail build on any violation - Wired into `gate` script (skill 07 `quality-gates.md`) -
seo-and-keywords.md 6.1 KB
--- name: "SEO and Keywords" version: "1.2.0" updated: "2026-04-23" description: "Full SEO engine: keyword research, competitor analysis, per-page primary+longtail targeting, Yoast-level checks, schema markup, internal linking, pSEO at scale. Runs every build." --- # SEO and Keywords Every page targets: 1 holy-grail keyword (high-volume) + 1-2 longtail phrases (lower competition, high intent) + semantic variations woven naturally. ## Keyword Research Workflow ### Step 1: Seed from Product `Domain → category → features → user problems`. E.g. `"instantidle.com" → container deployment → "docker hosting" → "cheap docker hosting"`. ### Step 2: Expand with APIs #### Google Autocomplete (FREE, no key) ```typescript async function getAutocompleteSuggestions(seed: string): Promise<string[]> { const url = `https://suggestqueries.google.com/complete/search?client=firefox&q=${encodeURIComponent(seed)}`; const [, suggestions] = await (await fetch(url)).json(); return suggestions; } // Expand with alphabet modifiers: `${seed} a`, `${seed} b`, etc. ``` #### Other tools - **DataForSEO** (~$0.01/keyword) — volume, CPC, competition, trends - **Google Search Console** (FREE) — real clicks, impressions, position - **Google Trends** — relative interest over time ### Step 3: Competitor Analysis 1. Identify 3-5 competitors 2. Scrape `sitemap.xml` → fetch pages → extract title / h1 / meta 3. Find gaps (keywords they rank for that we don't) + easy wins (high volume + low competition) ### Step 4: Selection Matrix - **Holy-grail** — highest volume relevant keyword - **Longtail 1** — lower competition, commercial intent - **Longtail 2** — informational intent, blog / resource page ## Per-Page Yoast Checklist (ALL GREEN) - **Title** — 50-60 chars, keyword at start, unique, includes brand. Format: `{Keyword} — {Supporting} | Brand` - **Meta description** — 120-156 chars, contains keyword, includes CTA, unique per page - **URL** — short, readable, keyword-rich, lowercase, hyphens. Good: `/docker-hosting` - **Headings** — one H1 with keyword; H2s with longtail/semantic variations; logical hierarchy; no skipped levels - **Content** — keyword in first 100 words; density 0.5-3% (natural); min 300 words (600+ preferred); Flesch ≥60; short paragraphs (2-4 sentences); subheadings every 200-300 words; transition words ≥30% of sentences - **Images** — alt text with keyword (natural); descriptive filename; width/height set; lazy load below-fold; WebP <200KB - **Internal links** — 2-3+ per page, descriptive anchors - **External links** — 1-2 outbound to authoritative sources, `target="_blank" rel="noopener"` - **Schema / JSON-LD** — 4+ per page minimum: Organization + WebSite + WebPage + domain-specific (FAQ, Product, BreadcrumbList, Article, HowTo, SoftwareApplication, DonateAction) - **Technical** — canonical URL, mobile-friendly, LCP <2.5s, HTTPS, no duplicate content, XML sitemap, robots.txt ## Readability Enforcement (Flesch ≥60) ### Yoast 9 Checks (All Automatable) - Flesch ≥60 | Sentence length avg ≤20 words | Paragraph length ≤150 words - Passive voice <10% | Transition words ≥30% of sentences | Consecutive sentences <3 with same opening word - Subheading distribution ≤300 words between H2/H3 | Text present | Exactly 1 H1 ### By Section Hero 80+ | Body 50-65 | Technical docs 40-50 | Legal 50+ | Errors 70+ | CTAs 80+ ## Programmatic SEO ```typescript // Dynamic pages: "Docker hosting in [city]" const cities = ['New York', 'San Francisco', 'London']; for (const city of cities) { // slug, title, h1, metaDescription all include city + keyword } ``` ### Internal Linking Strategy - Homepage → category pages → product pages | Blog → relevant products + related posts - Every page: 2-3 internal links min; anchor text = keyphrase variations, never "click here" - Orphan detection after build ## SEO Audit (Every Deploy) Playwright test verifying: title 30-60 chars · meta desc 120-160 chars · exactly 1 H1 · canonical URL · OG tags · JSON-LD ≥1 · no Lorem/TODO/placeholder · images have alt · internal links ≥3 · `robots.txt` 200 · `sitemap.xml` 200 ## API Stack ($8/mo total) - **Google Search Console** — Free — real clicks/impressions - **VebAPI** — $8/mo — competitor keywords, SERP - **Serper free tier** — Free — 2,500/mo SERP + PAA - **Google Suggest** — Free — keyword expansion - **SearXNG (self-hosted)** — Free — PAA questions - **DataForSEO** — $0.0006/query — bulk volumes at scale ## Google's SEO Rules (2026 Core Update) - Original research/proprietary data rewarded (Information Gain signal) - Write naturally, no stuffing - LCP <2.5s is a hard ranking factor - AI Overviews steal short-tail clicks — longtail more important than ever - Meta keywords tag ignored ## Longtail Strategy (Moz 2026) - Ultra-long-tail is "new normal"; short-tail triggers AI Overviews - Modifier stacking: `[product] + [use case] + [audience] + [qualifier]` - Problem-first keywords beat feature keywords - GSC: impressions >10 but position >10 = low-hanging fruit ## MANDATORY: Research BEFORE Writing 1. Identify 5-10 candidate keyphrases 2. Evaluate — volume, difficulty, intent, relevance 3. Select PRIMARY (1-4 words, unique to page) 4. Select 2-3 RELATED keyphrases 5. Map keyword → page URL (no duplicates) 6. Write content optimized from first sentence ## Keyword-to-Page Map ```typescript const keywordMap = { '/': { primary: 'main keyword', related: ['variation 1', 'variation 2'] }, '/pricing': { primary: 'pricing keyword', related: [...] }, }; // No two pages share same primary keyphrase ``` ## Rich Snippets Every page — Organization + WebSite(SearchAction) + WebPage + domain-specific schema. OpenSearch XML at `/opensearch.xml`. Validate with Google Rich Results Test. ## Ownership - **Owns:** keyword research, competitor analysis, per-page targeting, Yoast checks, readability, programmatic SEO, audit automation, internal linking, schema/JSON-LD, OG/Twitter tags, sitemap, robots.txt, keyword-to-page mapping - **Never owns:** content writing (→09, 22), visual design (→10), deployment (→08) -
SKILL.md 10.9 KB
--- name: "brand-and-content-system" description: "Extract real brands (Wayback for rebuilds). Copy system, headline/CTA rules, trust surfaces, legal pages, SEO+structured data, anti-AI-slop, microcopy, DESIGN.md, W3C DTCG tokens, pSEO 5 types, GEO/AI search." metadata: version: "2.1.0" updated: "2026-05-03" effort: "medium" model: "haiku" license: "Rutgers" compatibility: claude-code: ">=2.0.0" agentskills: ">=1.0.0" submodules: - ai-search-geo.md - build-breaking-rules.md - documentation-and-codebase-hygiene.md - email-templates.md - grammar-audit.md - per-route-metadata.md - seo-and-keywords.md - social-automation.md priority: 3 pack: "content" stage: stable triggers: - "brand" - "content" - "copy" paths: - "org:website_build" - "org:website_build" --- # 09 — Brand and Content System Extract real brand assets and apply the full copy system (anti-slop, SEO, trust surfaces, GEO) to every website build surface. > **Model migration note (pass-77, 2026-06-09)**: `DALL-E` → **GPT Image 1.5** + `GPT-4o` → **GPT Image 2 vision**. Per `platform.openai.com/docs/deprecations`. Brand extraction protocol unchanged. ## Brian's Brand Voice - Slogans: "Open-Source Wizardry. 100% Wizardry. 0% Robes." / "Often imitated, never duplicated." - Newsletter: "Lab Insights Journal" | Handle: @HeyMegabyte | Email: hey@megabyte.space / brian@megabyte.space - Tone: professional but irreverent, "Hey" not "Hi", first-person Megabyte Labs - Always "cross-platform" + "open-source" | Install Doctor: "single command" / "one-liner" - Hero: "[Topic] **Innovation**" | Footer CTA: "Let's Talk" | Rates: $140/hr ($70 nonprofit), $100/mo WordPress - Social: all platforms, "Megabyte Minis" YouTube, Dev.to, Patreon - Psychology: reciprocity (teach), social proof near CTAs, authority (depth/numbers), unity ("we/us"), Peak-End Rule ## Brand Extraction (Rebuilds) 1. Screenshot existing (Wayback if down). Extract logo/colors/fonts/tone. Never discard equity. 2. **Color extraction (NON-NEGOTIABLE)** — Screenshot with Playwright, GPT Image 2 vision extracts hex (logo priority), cross-ref logo, build palette, validate WCAG AA. NEVER invent, NEVER use Emdash defaults for clients, NEVER infer from category. 3. **Second-pass verification (BUILD-BREAKING — pre-deploy)** — After GPT Image 2 vision returns `{primary, secondary, accent}`: - Load logo PNG via sharp, sample dominant chroma via k-means k=5 ignoring transparent + near-white/near-black (top-3 cluster centroids in HSL) - For EACH color, compute min HSL hue-distance to top-3 logo chromas — if `min_hue_distance > 30°` AND saturation>0.2, FAIL w/ diagnostic - Re-run GPT Image 2 vision w/ corrective prompt naming top-3 logo chromas + demanding `primary` derived from one - Validator: `validate-color-from-logo.mjs` in `build_validators.ts` between brand-research and template-pick - NEVER ship primary color failing hue-distance check 4. **Logo-luminance + source-theme drives theme (NON-NEGOTIABLE)** — Two-signal: - Signal A: logo dominant-color luminance (WCAG formula) - Signal B: source-site dominant background luminance (Playwright screenshot of `body` background) - BOTH agree → match; disagree → source-site wins UNLESS source design score <7/10 (then logo wins) - High-quality source (≥7/10 via GPT Image 2 vision `detail:low`) → match source theme verbatim - Set theme BEFORE template selection. Reject palette where logo-on-bg contrast <4.5:1 (WCAG AA). - "Dark-first" applies to accent-rich Emdash/SaaS brands, NOT to logo-driven non-profit/serif clients or high-quality light-themed source brands 5. **Logo-vs-container contrast (BUILD-BREAKING — every render)** — Every logo render (header, footer, hero, modal, splash, mobile menu, sidebar) MUST contrast container bg by ≥4.5:1 on logo's dominant chroma (not transparent pixels) - Forbidden: white-text-logo on white/cream | dark-text-logo on dark/navy | low-saturation-logo on same-hue bg - Resolution: header AND footer themes chosen AFTER logo luminance scan. Dual-theme site needing SAME logo → ship TWO files (`brand-mark-light.svg` for dark bg, `brand-mark-dark.svg` for light bg) + CSS `<picture><source media>` swaps - Automate via skill 12 logo-variant-generator (Real-ESRGAN inversion or `magick -channel RGB -negate`; color logos w/ text → GPT Image 1.5 w/ "same logo on transparent bg w/ text inverted to <opposite-luminance>") - Validator: `validate-logo-contrast.mjs` — Claude Sonnet 4.6 vision samples logo bbox + container computed bg at 6bp + pixel sampling, fails if <4.5:1 6. **Brand-element extraction (logo is gold mine — extract DNA)** — GPT Image 2 vision returns `{font_family_guess, suggested_heading_font, suggested_body_font, font_weight, letterspacing, has_icon_mark, icon_mark_description, icon_mark_dominant_color, decorative_motif_description, motif_extractable (bool)}` - Matched Google Font → `--font-heading` site-wide - `motif_extractable=true` → crop icon-only region (`magick logo.png -alpha extract -trim +repage`), upscale 2-4× via Real-ESRGAN / GPT Image 1.5 variation, save as `assets/brand-splash.png` + `assets/brand-mark.png` 7. **Logo singularity (BUILD-BREAKING — exactly ONE logo file per container)** — Every logo container renders EXACTLY ONE logo asset - Never composite two logo sources side-by-side; never stack icon-mark + wordmark as separate `<img>` tags; never render `apple-touch-icon.png` next to `logo.svg` in same container - Composition at asset-prep time via `magick logo-mark.png logo-wordmark.png +append`, NOT at render time - Validator: `validate-logo-singularity.mjs` parses `dist/` HTML; count>1 descendant `<img>` or inline `<svg>` in same logo container = FAIL 8. **Logo (NON-NEGOTIABLE)** — Every project needs premium logo. See Skill 12 for full process. 9. **Audit** — logo found + rated ≥7/10 + works 16-512px, colors EXTRACTED, palette WCAG AA, typography + tone + messages identified. ## Brand Extraction from Physical Assets (LOCAL BUSINESS — NO WEBSITE) - Street View Static API: `https://maps.googleapis.com/maps/api/streetview?size=1200x800&location={lat},{lng}&source=outdoor` - Places photos: filter `types: ["exterior", "storefront"]` - GPT Image 2 vision on storefront — prompt: `"Extract brand identity from this business storefront photo. Return JSON: {sign_text, sign_font_style, primary_color (hex), secondary_color (hex), accent_color (hex), logo_description, overall_aesthetic, confidence (0-1)}"` - Business cards: GPT Image 2 vision extracts logo (crop region), colors (exact hex), font, tagline, NAP for verification. - Color extraction priority: 1. Signage → 2. Logo → 3. Storefront awning/trim → 4. Interior decor → 5. Vehicle wrap → 6. Business card → 7. Category default (LAST RESORT). Each tagged with `color_source`. - Font matching from signage: Script → Dancing Script | Serif → Playfair Display | Modern sans → Inter | Hand-lettered → Caveat. Never use exact proprietary fonts. ## Brand Inference (New Products) - Dev tool → technical/dark/monospace | SaaS → professional/clean/cards | Agency → confident/bold - E-commerce → friendly/product-focused | Nonprofit → warm/impact imagery | API → technical/docs-forward - Emdash defaults (NOT for clients): `#00E5FF`, `#50AAE3`, `#060610`. Sora / Space Grotesk / JetBrains Mono. ## Anti-AI-Slop Detection (MANDATORY SCAN) **Banned copy words:** `delve | leverage | unleash | revolutionize | best-in-class | cutting-edge | discover | innovative | seamless | robust | synergy | elevate | empower | transformative` **Banned patterns:** "Welcome to" | "Discover [product]" | vague aspirational headlines | hedging ("may help you," "can potentially") | generic superlatives **Banned design tells:** Inter as sole font | purple-blue gradients | uniform 16px border-radius everywhere | centered everything | Hero/Lucide as sole icon set | abstract 3D blobs | uniform fade-in on all elements | plastic AI stock photos Fix: ask "Would the founder actually say this?" No → rewrite. ## Copy System - Headlines — benefit-first, specific, numbers, max 8 words - Subheadings — expand promise, 15-25 words - Body — one idea/paragraph, 2-4 sentences, active, concrete, benefit-oriented - CTAs — specific action verb first, gradient primary + ghost secondary, above fold + page end - Never: "Click here" | "Submit" | "Learn more" - Personality mapping — Expert → precise industry terms | Direct → short declarative | Pragmatic → outcomes/implementation - Vocabulary: always-use / never-use / prefer-over lists | max sentence: 25 words - Banned structures: passive voice, hedging, em dashes mid-sentence | monthly drift audit ## Microcopy System - Error messages — [What happened] + [What to do]. Empathetic, solution-oriented. "Payment failed. Try a different card or contact support." Never "Error 500" or jargon. Flesch 70+. - CTAs — action verb first, max 3 words preferred: "Start building" | "Ship today" | "Get access" - Empty states — acknowledge absence + suggest action: "No projects yet. Create your first one." Never just "No results" - Toasts — past tense success ("Project created"), present in-progress ("Saving…"), plain-language errors - Form labels — noun phrases, sentence case. Helper text: one line, 10 words max ## Machine-Readable Brand Documentation - **DESIGN.md** (6 sections): 1. Visual Theme | 2. Color Palette (hex + role) | 3. Typography (family + scale + weight) | 4. Spacing + Layout | 5. Components (states + variants + props) | 6. Elevation + Shadows - **Tokens**: W3C DTCG JSON format, single source of truth. Generated from `tokens.json` → CSS vars + Tailwind config. ## SEO + Structured Data ### Per-page MUST have - Title 50-60 chars keyphrase-first | meta desc 120-156 chars | canonical - OG image 1200×630 branded card | one H1 in prerendered HTML - JSON-LD: WebPage floor; Org/BreadcrumbList/FAQPage/Person/Product/Service only when real entities ### pSEO 5 page types - Integration (`/integrations/{tool}`) | Comparison (`/compare/{a}-vs-{b}`) | Use-case (`/for/{audience}`) | Template (`/templates/{type}`) | Location (`/{city}-{service}`) - Each: unique H1 + meta desc + 800+ unique words + 1 unique image + 3+ internal links + 1+ outbound citation. Never templatize verbatim. ### GEO / AI search - Quotable answer blocks 40-60 words (LLMs cite) - FAQPage schema highest AI-citation rate - JSON-LD facts MUST also appear as visible HTML text | lead paragraphs answer query in <40 words - EEAT: author bio + Person schema + `sameAs` + dated revision + ownership ## Trust Surfaces - Real testimonials w/ verifiable attribution | license/accreditation badges - Security.txt + privacy + terms | Person JSON-LD w/ sameAs | About + Team pages with real bios ## Legal Pages (required) - /privacy | /terms | /accessibility ## See submodules: email-templates, social-automation, seo-and-keywords, documentation-and-codebase-hygiene, per-route-metadata, grammar-audit, build-breaking-rules. -
social-automation.md 6 KB
--- name: "Social Automation" version: "1.1.0" updated: "2026-04-23" description: "Auto-post via Postiz (self-hosted Coolify) on deploy. X/Twitter, LinkedIn, Reddit, GitHub, Threads, TikTok, Pinterest+. Platform-specific copy in Brian's voice, OG images, Playwright verification." --- # Social Automation via Postiz ## Postiz Instance - **URL** — https://postiz.megabyte.space - **Connected platforms** — X/Twitter, LinkedIn, Reddit, GitHub, Threads, Facebook, TikTok, Pinterest, Dribbble, Discord, Slack, Mastodon, Telegram, Beehiiv ## When to Auto-Post - New product launch (first deploy of a domain) - Major feature additions (new sections, new integrations) - Milestone achievements (donation goals met, impact numbers) - **NOT for** — bug fixes, style tweaks, skill updates, internal changes ## Post Generation 1. Summarize the change in 1-2 sentences (Brian's voice — sharp, punchy) 2. Include the site URL 3. Include the OG image URL for visual preview 4. Add relevant hashtags (max 5) 5. Customize per platform: - **X/Twitter** — 280 chars max, punchy, 2-3 hashtags - **LinkedIn** — professional tone, 1-2 paragraphs, link in body - **Reddit** — subreddit-appropriate title, link post - **Facebook** — casual, include image, tag @HeyMegabyte ## Postiz API ```bash # Create a post curl -X POST "https://postiz.megabyte.space/api/posts" \ -H "Authorization: Bearer POSTIZ_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "content": "Post text here", "platforms": ["twitter", "linkedin"], "media": ["https://domain.com/og-image.png"], "schedule": "now" }' ``` ## Quality Verification After posting, verify the post rendered correctly: 1. Get the public share URL from the Postiz API response 2. Use Playwright to screenshot the share URL 3. Visual inspection — verify image loaded, text not truncated, link works 4. If issues found — debug, fix, repost ## Iterative Improvement Track which posts get engagement. Over time: - Which platforms drive the most traffic? - Which post formats get the most engagement? - What time of day performs best? Log this in PostHog for analysis. ## Coolify Access Postiz runs on Coolify. If the API is down: 1. Check Coolify status — `curl coolify.megabyte.space/api/v1/services` 2. Restart the service if needed via Coolify API 3. Log the outage for future reference ## MCP Tools Available ### Postiz MCP (`mcp__postiz__*`) - **`integrationSchedulePostTool`** — schedule a post to connected platforms with date / time - **`integrationList`** — list all connected platform integrations - **`integrationSchema`** — get schema for a specific integration (required fields, limits) - **`generateImageTool`** — generate social preview images with AI - **`generateVideoTool`** — generate short video clips for social - **`generateVideoOptions`** — get available video generation options - **`videoFunctionTool`** — execute video processing functions - **`triggerTool`** — trigger immediate post (bypass scheduling) - **`ask_postiz`** — ask Postiz AI for post copy suggestions ### Coolify MCP (`mcp__coolify__*`) — for Postiz service health - **`diagnose_app`** — diagnose Postiz service health - **`get_service`** — get Postiz service details - **`control`** — restart Postiz if it's down ### PostHog MCP (`mcp__posthog__*`) — for engagement tracking - **`authenticate`** — connect to PostHog for social analytics ## Posting Workflows ### Launch Day Social Sequence When a new site launches, execute this sequence: 1. **T+0 min** — Post to X/Twitter (highest immediacy audience) 2. **T+15 min** — Post to LinkedIn (professional framing) 3. **T+30 min** — Post to Reddit (relevant subreddit, link post) 4. **T+1 hr** — Post to Facebook, Threads (broader reach) 5. **T+2 hr** — Post to Discord, Slack channels ```typescript // Example: Schedule the full launch sequence const platforms = ['twitter', 'linkedin', 'reddit', 'facebook', 'threads']; const delays = [0, 15, 30, 60, 120]; // minutes for (let i = 0; i < platforms.length; i++) { const scheduleTime = new Date(Date.now() + delays[i] * 60000); // Use mcp__postiz__integrationSchedulePostTool with platform + scheduleTime } ``` ### Social Preview Image Generation Before posting, generate an OG-quality social image: 1. Call `mcp__postiz__generateImageTool` with site title + tagline 2. If custom image exists (OG image from site), use that instead 3. Verify image dimensions — 1200×630 for OG, 1080×1080 for Instagram / Threads 4. Fallback — screenshot the hero section via Playwright at 1200×630 ## Computer Use Integration Use `mcp__computer-use__*` tools for visual verification of posted content: 1. **Post verification** — after scheduling via MCP, open the platform in browser and screenshot to confirm the post rendered with correct image, text, and link 2. **Engagement monitoring** — screenshot Postiz dashboard at `https://postiz.megabyte.space` to check engagement metrics after 24 hours 3. **Platform-specific debugging** — if a post looks wrong on a specific platform, screenshot the live post to diagnose formatting issues ``` Workflow: Post → Wait 60s → Screenshot platform → AI visual check → Flag issues ``` ## Acceptance Criteria 1. Post reaches all specified platforms — `mcp__postiz__integrationList` confirms connected, API returns 200 per platform 2. OG image renders correctly — Playwright screenshot of share URL shows image loaded (not broken / missing) 3. Text not truncated — post text is within platform character limits (280 X, 3000 LinkedIn, 300 Reddit title) 4. Link is clickable and resolves — fetch the URL in the post body, confirm 200 response 5. Hashtags present and relevant — 2-5 hashtags per post, no banned / spam tags 6. Launch sequence timing correct — posts staggered per the launch day sequence (not all simultaneous) 7. No duplicate posts — check Postiz post history, no identical content within 24 hours 8. PostHog event tracked — `social_post_published` event fires with platform + URL properties
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.