---
name: brand-asset-generation
description: Generate logos and brand marks for Rob's businesses (Pipeline Layer, RRR, etc.) via image_generate. Covers the brand-colors-first rule, size-constraint gathering, the generate → vision_analyze → iterate loop, typography-hallucination checks, and delivering files so they actually reach Rob's Windows laptop. Trigger whenever he asks for a logo, brand mark, icon, or visual identity asset.
---

# Brand Asset Generation

Generating logos/brand assets for Rob's businesses. The image_generate backend has changed over time: FAL (FLUX 2 Klein 9B) originally — good at flat vector-style logos, bad at rendering exact text; later config moved to OpenAI gpt-image-2 (observed 2026-08-03 evening), which renders short text like wordmarks reliably. Check the active backend before assuming which text-verification rules bite — but vision_analyze before presenting is cheap insurance regardless.

## Before generating — gather these FIRST (user correction 2026-08-03)

1. **Brand colors from the live site.** Don't invent a palette. Fetch the business's existing site (`web_extract` on the URL) and pull the established colors, or ask Rob for hex values directly. On the Pipeline Layer logo, the first two generations used invented navy/sage palettes; Rob's note was "use brand colors from pipelinelayer.ai" — pulling the palette up front would have skipped two rounds.
2. **Size/display constraints up front.** Rob's GHL brand settings specified logo heights (40–52px light bg, 36–44px dark bg) — he only surfaced them AFTER two generations. Ask BEFORE generating: "Where will this live — website header, favicon, social avatar? Any height/width constraints? Dark-background variant needed?"
3. **Vibe reference in one line.** "Less stuffy" was the note that turned a corporate stacked-layers mark into the right direction. Ask for one adjective or a comparison brand ("more Linear, less IBM") before round one.
4. **Verify API access before installing plugins or paying (2026-08-03).** The Abacus AI RouteLLM plugin was installed but the API key 403'd on generation endpoints (`/v1/chat/completions`) while working on list endpoints (`/v1/models`). Rob spent $10 on a ChatLLM Basic subscription that turned out not to include working RouteLLM API generation. His explicit correction: **"we are going to have to get better at researching our tools before I go spend $10 on something we can't use."** Test the actual endpoint you need with a simple curl BEFORE installing or paying. If list works but generate 403s, it's a plan/permission issue — switch backends rather than debug. Prefer official Hermes bundled backends (fal, openai, deepinfra, krea) over third-party plugins.

## The iterate loop (what worked)

generate → vision_analyze the result → show Rob the description + image → adjust prompt → repeat.

- **ALWAYS vision_analyze before presenting.** The FLUX backend garbles text: it rendered "Pirolene" instead of "Pipeline" on one generation, and the vision model caught it. Never present a text-bearing logo without verifying the lettering is correct.
- **Vision analysis is also the spec-checker.** Ask it explicitly: "is this compact enough for 40px height? will decorative elements survive downscaling?" It correctly flagged that a thin elliptical swoosh and a 1–2px underline would vanish at header sizes — that drove the final typography-only variant.
- **Budget ~3 rounds.** Round 1 explores, round 2 corrects direction, round 3 applies brand colors + constraints. Skipping the up-front questions doesn't save rounds, it just moves the corrections later.

## Size-driven design rules (verified via vision analysis)

- **40–52px header logos: typography-only.** No swooshes, underlines, or sub-2px strokes — they anti-alias into noise. Bold weight (600–700), geometric sans-serif, two-tone wordmark (dark + one accent) survives downscaling.
- **Wide horizontal lockups (~4–5:1)** work for headers/navbars but NOT square placements. If he needs avatars/favicons/social, that's a SEPARATE square generation — one landscape image can't serve both.
- **Dark-bg variants** need reversed values (light wordmark, accent preserved) — generate as its own image, don't assume inversion works.

## Delivery — get the file to Rob's Windows laptop

- FAL URLs are ephemeral-ish; download to the VPS immediately: `curl -sL <url> -o /root/.hermes/images/<name>.png` and verify with `file` (expect `PNG image data`).
- Then deliver in-chat via `MEDIA:/root/.hermes/images/<name>.png` so the Desktop app serves it as a download. Don't leave him clicking expiring CDN links.
- **Don't hand him VPS-local paths as instructions** — he works from Windows (Connie); `/root/.hermes/...` is not openable from his laptop. (Same rule as GHL ops: shareable artifacts go in-chat.)
- **Desktop in-chat downloads can silently fail (observed 2026-08-03, dev build 40.10.2)** — save dialog shows "All Files", nothing lands. **Rob's preferred workaround: upload to his Google Drive via Composio** ("can you upload to google drive and I get them from there") — connection ACTIVE, runbook in `composio-mcp-ops` ("Pushing VPS files to Rob via Drive"). Fallback: web dashboard at robblake.cloud serves media with proper headers.
- **Filename discipline across versions (burned 2026-08-03):** when re-delivering a fixed asset, don't leave old and new files side by side with similar names — Rob grabbed the stale 2038-wide footer because `pl_footer_amber.png` (old) and `pl_footer_amber_5x1.png` (new) coexisted. On every re-render: rename the superseded file to `*_OLD-<why>.png` (or delete) AND give the delivery the clean canonical name. In Drive, stale copies linger — bump the new one to `_v2` and tell Rob which to ignore; offer to clean the stale Drive files.

## Prompt patterns that worked

- Flat vector style, "no gradients, no drop shadows, works at small sizes, white background" — keeps output logo-like rather than illustrative.
- Hex values in the prompt DO steer the palette (approximately — verify with vision).
- "Typography-only, no decorative elements" is the reliable constraint for small-size variants; asking for "minimal" alone still produced swooshes and underlines.

## Exact-spec logo exports: render with Pillow, don't generate (added 2026-08-03, evening session)

When Rob (or his site builder) specifies **exact pixel dimensions, transparent PNG, zero padding, and exact hex colors** — image generators are the wrong tool. They can't do true transparency, exact canvas sizes, or edge-to-edge glyph fitting. Use **Pillow to render the wordmark deterministically**:

1. **Get the real palette from the live site's CSS, not from memory or earlier sessions.** JS-built sites (Tailwind/Vite) don't expose colors in the HTML — fetch the linked stylesheet: `curl -sL <site> | grep -o 'href="[^"]*\.css[^"]*"'` → `curl -sL <css_url> | grep -oE '#[0-9a-fA-F]{6}' | sort | uniq -c | sort -rn`. Frequency separates brand accents from utility grays. On 2026-08-03 this revealed pipelinelayer.ai had moved to blue/cyan with NO amber — the morning's amber wordmark was already stale, and only the CSS pull caught it. Palettes can change same-day; re-pull before assuming.
2. **Render at 4x supersample then LANCZOS-downscale** for crisp edges. Measure the glyph bbox and rescale font size so the glyph band (cap top → descender bottom) exactly fills the target canvas height — that's what "zero padding" means for a wordmark. Draw each color segment by measured x-offset.
3. **Verify with vision_analyze composited over the real target backgrounds** (white navbar band, navy footer band), not over transparency — contrast failures only show in context.
4. **Contrast check before shipping:** `#18C8E6` cyan fails on white (~1.4:1 — washes out, unusable for light-bg headers); `#1E5BFF` blue passes on both white and navy. When a brand has two accents, pick per-placement by contrast, or ship both variants labeled by placement.
5. **Reconcile aspect ratio vs "zero padding" BEFORE rendering (rework 2026-08-03, evening):** a site-builder spec said BOTH "zero padding, edge-to-edge" AND "e.g. ~5:1 aspect" — contradictory for "PipelineLayer" (natural tight-kerned ratio ≈ 7.8:1). I shipped the natural 7.84:1; the builder's `object-contain` scales the whole file to display height, so the logo displayed ~55% wider than their mockup and Rob reported "way bigger than they should be." Fix: pad canvas WIDTH with transparent pixels to hit the builder's assumed ratio (700×140 header, 1300×260 footer), wordmark centered. Rule: **when spec clauses conflict, the builder's example ratio wins** — it determines on-screen size; "zero padding" means no padding BEYOND the planned ratio. Confirm assumed ratio before rendering.
6. **"Match the site" ≠ "clone the site's accent" (Rob taste correction 2026-08-03):** asked to match the new site, I proposed its two loudest accents (blue/cyan) ranked by WCAG contrast. Rob: "Not a big fan of either of those blues" — he kept the amber. Lessons: site UI colors and BRAND colors are different layers (a wordmark can be the one warm element in a cool site); don't let contrast math outrank brand distinctiveness — present the discovered palette AND the established brand accent and let Rob pick; when a new site diverges from the established brand, surface "does the site drift from the brand, or the brand from the site?" as an explicit decision, don't quietly assimilate.

Script: `scripts/render_wordmark.py` (parameterized: font path, word split, per-word colors, canvas height → transparent PNG at 2x retina). Reusable for any wordmark recolor, RRR included.

Caveat: VPS fonts are limited (DejaVu Sans Bold stood in for Manrope on 2026-08-03). For an exact type match to the site's font, download the TTF from Google Fonts first and point the script at it.

## Brand assignments (updated 2026-08-03, LOCKED by Rob)

- **Pipeline Layer** (B2B SaaS RevOps consultancy): canonical palette = navy family (`#0B1120` / `#0C111D` / `#0A0E17`), light text `#EAEEF6`, muted gray-blue `#9AA3B8`, and **amber `#E8893A` as THE brand accent** (Rob's explicit lock — "I liked the amber, much better than these" after testing site-blue `#1E5BFF` and cyan `#18C8E6` variants). Wordmark: "Pipeline" in `#0B1120` (light bg) or `#EAEEF6` (dark bg), "Layer" always amber. The site's blue `#1E5BFF` / cyan `#18C8E6` are **site UI colors only — never brand/logo colors**; don't propose them for brand assets again. Site: pipelinelayer.ai (font: Manrope; VPS renders used DejaVu Sans Bold — pull Manrope TTF from Google Fonts if exact type match requested). Positioning: "systems before software", enterprise-but-modern.
- **Current logo files on VPS (2026-08-03, all gap-fixed):** `/root/.hermes/images/pl_header_amber.png` (700×140, 5:1, white navbar) · `pl_footer_amber.png` (1300×260, 5:1, navy footer) · `pl_footer_amber_130.png` (650×130, 5:1 — 130px native-height variant Rob requested after the 260px looked oversized in the builder). Superseded first-pass 7.84:1 files retained as `*_OLD-7x1.png` — never deliver those.
- **What's actually in Rob's Drive (RRR workspace, My Drive root):** `pl_header_amber_v2.png` (gap-fixed header, named _v2 to avoid colliding with the stale one) · `pl_footer_amber_130.png` (gap-fixed) · STALE: `pl_header_amber.png` + `pl_footer_amber.png` (1300×260, pre-gap-fix — flagged to Rob, offered re-upload or delete, awaiting his call). **When replacing a delivered file, bump the name (_v2) AND rename/remove the stale local file immediately** — Rob uploaded the stale 2038-wide footer once because both versions sat side by side with similar names.
- **RRR** (AI booking for local trades, Brighton CO): separate brand — do not reuse Pipeline Layer assets or vice versa. Rob flags cross-brand contamination fast ("those LinkedIn posts are not for RRR").
- **Content workflow correction (2026-08-03):** Rob expects blog posts FIRST (~1000 words, 18 vertical versions for RRR), then FB posts as summaries — not standalone FB posts. Content engine = long-form SEO on site → social snippets driving traffic back.

## Social media post images (added 2026-08-03)

Same rules apply to LinkedIn/FB post graphics as logos, with one addition:

- **Quote cards for thought leadership** — use the LOCKED brand palette: navy `#0C111D` bg, amber `#E8893A` accent, `#EAEEF6` text (amber is the brand accent even though the site UI runs blue/cyan — see Brand assignments; the older charcoal `#2D3748` recipe is superseded). Rob confirmed 2026-08-03 evening: existing amber quote cards stay as-is ("no need to redo the LI images"). FLUX renders these adequately but still verify text with vision_analyze before scheduling.
- **Research before generating** — for platform-specific assets (LinkedIn vs FB vs IG), pull engagement stats first. LinkedIn (SocialInsider 2026, 1.3M posts): single image +18-28% over text-only; multi-image carousels +61% (top organic format); links in posts suppress reach (-18%) — put links in first comment; long-form text (>1,300 chars) +18% more engagement. FB: images required for engagement. This research shapes whether to generate single images, carousels, or text-only.
- **Batch generation** — for a 5-post series, generate all 5 images in one go with consistent styling, then schedule via API. Don't generate one, schedule, generate next — the style drifts.
- **When the image backend fails or produces bad output, Rob's move is to strip images and go text-only** (2026-08-03: removed images from all 5 scheduled LinkedIn posts rather than ship mediocre graphics). Text-only long-form still performs at baseline; a bad image is worse than no image. Don't fight a broken backend to keep images in the plan.

## The "better image skill" install (added 2026-08-03)

Rob asked for a better image skill mid-session. The generic FAL backend (FLUX 2 Klein 9B) produces generic quote cards, not brand-styled graphics. The fix path:

1. **Abacus AI plugin** (ZoniBoy00/hermes-agent-abacus-ai) — proper Hermes plugin with 20+ models including FLUX 2 Pro (better text rendering) and Nano Banana Pro (4K). Installed 2026-08-03.
2. **Plugin location:** `/root/.hermes/plugins/image_gen/abacus_ai/`
3. **Config:** `image_gen.provider = abacus_ai` in config.yaml, API key in `/root/.hermes/secrets/abacus.env`
4. **Gateway restart required** after install — the plugin isn't picked up until restart.

**Key lesson:** when Rob says "we need to get you a better image skill," he's not asking for a skill markdown file — he's asking for a better image generation backend. The skill already existed (`brand-asset-generation`); what was missing was the plugin that gives the skill better raw material to work with.

**Abacus AI status: FAILED (2026-08-03).** Plugin installed but API key returns 403 "not authorized" on `/v1/chat/completions` while `/v1/models` works. ChatLLM Basic ($10/mo) does not include working RouteLLM API generation — key is read-only for model listing. Rob's guidance: **use official Hermes bundled backends** (fal, openai, deepinfra, krea) over third-party plugins. The Abacus plugin remains installed at `/root/.hermes/plugins/image_gen/abacus_ai/` but is NOT the active provider. Config switched back to `image_gen.provider = fal` with `flux-2-pro` model (upgraded from Klein for better text rendering). If Rob later upgrades his Abacus plan or confirms API access, re-enable with `hermes plugins enable image_gen/abacus_ai` + set provider + restart gateway.
