From e7a62e7b9590a2839d9e8ace50a70b0fe99b3b10 Mon Sep 17 00:00:00 2001 From: Paul Keen Date: Sat, 18 Jul 2026 22:37:24 +0200 Subject: [PATCH 1/2] docs(okf): add blog/site knowledge sections to the OKF bundle Fold a separately-produced Hugo blog/site OKF bundle into the existing course bundle as two new sibling sections plus five workflow concepts. Docs-only; no course-side concepts changed. - architecture/: hugo-site, css-pipeline, blog-list-page, cover-image-pipeline, seo-meta-tags - content-strategy/: icp-primary-website-target, voice-guide - workflows/: blog-pipeline, linkedin-post-pipeline, css-maintainability-plan, visual-scroll-gate, testing Root index + workflows index list the new entries; dated log entry appended. Validated conformant via okf:validate --strict (25 concepts). Co-Authored-By: Claude Opus 4.8 (1M context) --- .okf/architecture/blog-list-page.md | 30 +++++++++ .okf/architecture/cover-image-pipeline.md | 46 ++++++++++++++ .okf/architecture/css-pipeline.md | 50 +++++++++++++++ .okf/architecture/hugo-site.md | 44 +++++++++++++ .okf/architecture/index.md | 7 +++ .okf/architecture/seo-meta-tags.md | 24 +++++++ .../icp-primary-website-target.md | 31 ++++++++++ .okf/content-strategy/index.md | 4 ++ .okf/content-strategy/voice-guide.md | 31 ++++++++++ .okf/index.md | 4 +- .okf/log.md | 12 ++++ .okf/workflows/blog-pipeline.md | 62 +++++++++++++++++++ .okf/workflows/css-maintainability-plan.md | 48 ++++++++++++++ .okf/workflows/index.md | 5 ++ .okf/workflows/linkedin-post-pipeline.md | 41 ++++++++++++ .okf/workflows/testing.md | 48 ++++++++++++++ .okf/workflows/visual-scroll-gate.md | 53 ++++++++++++++++ 17 files changed, 539 insertions(+), 1 deletion(-) create mode 100644 .okf/architecture/blog-list-page.md create mode 100644 .okf/architecture/cover-image-pipeline.md create mode 100644 .okf/architecture/css-pipeline.md create mode 100644 .okf/architecture/hugo-site.md create mode 100644 .okf/architecture/index.md create mode 100644 .okf/architecture/seo-meta-tags.md create mode 100644 .okf/content-strategy/icp-primary-website-target.md create mode 100644 .okf/content-strategy/index.md create mode 100644 .okf/content-strategy/voice-guide.md create mode 100644 .okf/workflows/blog-pipeline.md create mode 100644 .okf/workflows/css-maintainability-plan.md create mode 100644 .okf/workflows/linkedin-post-pipeline.md create mode 100644 .okf/workflows/testing.md create mode 100644 .okf/workflows/visual-scroll-gate.md diff --git a/.okf/architecture/blog-list-page.md b/.okf/architecture/blog-list-page.md new file mode 100644 index 000000000..2ec5289cc --- /dev/null +++ b/.okf/architecture/blog-list-page.md @@ -0,0 +1,30 @@ +--- +type: API Endpoint +title: Blog Index / Listing Page +description: Hugo template rendering the JTWay blog index, tag pages, and post thumbnails. +resource: themes/beaver/layouts/blog/list.html +tags: [hugo, blog, layout] +timestamp: 2026-07-12T00:00:00Z +--- + +# Overview + +`themes/beaver/layouts/blog/list.html` renders the blog index +(`/blog/`) and tag taxonomy pages (`/blog/tags/:slug/`). It loads its +own CSS bundle (`blog-list`) via the `blog-list-css-resources.html` +partial and the shared `assets/css-processor.html` partial (see +[css-pipeline](/architecture/css-pipeline.md)). + +For tag pages (`.Type == "tags"`), it falls back to the blog section's +own cover image (`$blogIndex.Params.metatags.image`) via +[cover-image-pipeline](/architecture/cover-image-pipeline.md) for the +page-level `og:image`/`twitter:image` meta tags. + +Post thumbnails on the list page are rendered by +[img-cropped partial](/architecture/cover-image-pipeline.md), not by +`cover_image.html` directly — thumbnails need responsive `` +srcsets, cover meta tags need only a single canonical URL. + +# Citations + +[1] `themes/beaver/layouts/blog/list.html` diff --git a/.okf/architecture/cover-image-pipeline.md b/.okf/architecture/cover-image-pipeline.md new file mode 100644 index 000000000..d7de70190 --- /dev/null +++ b/.okf/architecture/cover-image-pipeline.md @@ -0,0 +1,46 @@ +--- +type: Playbook +title: Blog Cover Image Pipeline +description: How a blog post's cover image is generated (JetVelocity design system), stored, and rendered as thumbnail vs og:image. +tags: [images, seo, design, cdn] +timestamp: 2026-07-12T00:00:00Z +--- + +# Overview + +Cover images are generated at a 2400×1260 source PNG (2x retina) using +the **JetVelocity "Obsidian Engine"** design system defined in +`.stitch/design.md`, and follow a mandatory 6-slot layout (brand/category, +year pill, 3-line gradient headline, visual ember, tag chips, status +chip). Hugo derives the 1200×630 `og:image`, responsive +640/960/1920 WebP/JPG variants, and a 360×189 thumbnail from that +source at build/CDN time. + +Two distinct rendering paths consume the same source image: + +- **`page/cover_image.html`** — emits only the `og:image` / + `twitter:image` meta tags. If `site.Params.cdn.enabled`, it uses the + CDN-resized resource's own `Permalink`/`Width`/`Height` directly; + otherwise it falls back to a local Hugo `.Fit "512x512 jpeg"`. +- **`partials/blog/img-cropped.html`** — renders the actual + `` thumbnail markup used on cards/list pages. It resolves + the source image from `metatags.image` (primary convention), falling + back to `cover_image`, `cover`, then `featured_image` in that order. + When the CDN is enabled it requests WebP + JPG at both a fixed mobile + width (`160px`, i.e. 80px × 2 retina) and a caller-supplied retina + desktop width via `partial "cdn/url"` calls (see + `docs/workflows/cdn-image-proxy.md`), with `loading="lazy" decoding="async"`. + Without a source image it falls back to an aspect-preserving resize + with `object-fit: contain` letterboxing. + +Any new cover image or thumbnail change is subject to the +[visual-scroll-gate](/workflows/visual-scroll-gate.md) — visual +defects (clipped text, wrong aspect, stale badges) are invisible to +text-only validation. + +# Citations + +[1] `themes/beaver/layouts/partials/page/cover_image.html` +[2] `themes/beaver/layouts/partials/blog/img-cropped.html` +[3] `.stitch/design.md` +[4] `docs/workflows/cover-images.md` diff --git a/.okf/architecture/css-pipeline.md b/.okf/architecture/css-pipeline.md new file mode 100644 index 000000000..a321353f1 --- /dev/null +++ b/.okf/architecture/css-pipeline.md @@ -0,0 +1,50 @@ +--- +type: Service +title: CSS Build Pipeline (PostCSS + per-bundle PurgeCSS) +description: PostCSS pipeline that concatenates per-page CSS resource slices and purges unused rules per bundle before shipping. +resource: postcss.config.js +tags: [css, build, performance] +timestamp: 2026-07-12T00:00:00Z +--- + +# Overview + +Each Hugo template loads exactly ONE fingerprinted CSS bundle, built by +concatenating that template's resource slice (a list of source CSS +files declared in a `*-css-resources.html` partial) and running it +through PostCSS. PurgeCSS runs **per bundle**, reading `hugo_stats.json` +(see [hugo-site](/architecture/hugo-site.md)) to strip unused +selectors, so each page ships only the subset of CSS it actually uses. + +This per-bundle purge is the reason source-file line counts predict +nothing about shipped bytes: consolidating shared source files can +*grow* shipped bytes (measured: a shared FL foundation added +7.1–9.8 KB +gzip first-visit per page). Any size/perf claim must be validated on +**compiled + gzip per-page payload**, never raw source line counts — +see [css-maintainability-plan](/workflows/css-maintainability-plan.md). + +# Legacy liability: FL-Builder export CSS + +A large portion of shipped CSS (~75K lines at audit time) is +FL-Builder page-builder export CSS: `.fl-node-{hash}` selectors keyed +to opaque generated IDs, effectively obfuscated and un-editable by +hand. These files are being retired page-by-page (strangler pattern), +not optimized in place. The live burn-down list and per-bundle +ownership is tracked in +`docs/projects/2509-css-migration/css-bundle-ownership-map.md`. + +# Consolidation rules + +- Never modify vendor files (`css/vendors/*.min.css`) or dynamic + templates (`css/dynamic-*.css`). +- Preserve cascade order: Critical → Layout → Component → Theme → + Footer. +- Extract whole rule sets; keep page-specific `.fl-node-{hash}` + selectors in their original files. +- Run `bin/rake test:critical` after each extraction. + +# Citations + +[1] `postcss.config.js` +[2] `docs/workflows/css-consolidation.md` +[3] [css-maintainability-plan](/workflows/css-maintainability-plan.md) diff --git a/.okf/architecture/hugo-site.md b/.okf/architecture/hugo-site.md new file mode 100644 index 000000000..825bcb474 --- /dev/null +++ b/.okf/architecture/hugo-site.md @@ -0,0 +1,44 @@ +--- +type: Service +title: JetThoughts Hugo Site +description: Hugo static site generator setup for the JetThoughts marketing site and blog (JTWay). +resource: config/_default/hugo.toml +tags: [hugo, build, config] +timestamp: 2026-07-12T00:00:00Z +--- + +# Overview + +The site is built with [Hugo](https://gohugo.io) using the `beaver` theme +(`themes/beaver/`). Config lives under `config/_default/`, with +`hugo.toml` as the entry point. Content lives in `content/`, with blog +posts under `content/blog//index.md`. + +Key config choices (`config/_default/hugo.toml`): + +- `baseURL = "https://jetthoughts.com/"`, `theme = "beaver"`. +- Permalinks: pages at `/:slug/`, blog posts at `/blog/:slug/`, tag + taxonomy at `/blog/tags/:slug/`. +- `[build] writeStats = true` — Hugo writes `hugo_stats.json`, which + PostCSS/PurgeCSS reads to know which CSS classes are actually used + on the page (see [css-pipeline](/architecture/css-pipeline.md)). +- Goldmark markdown renderer runs with `unsafe = true` (raw HTML in + markdown is passed through). +- Highlighting uses the `dracula` Chroma style; **fenced ERB code + blocks must use the `` ```html `` alias, not `` ```erb ``** — Chroma + has no `erb` lexer alias, so `erb` fences silently lose the syntax + highlight wrapper and render invisible light-on-light text. + +# Build & Test + +| Command | Purpose | +|---|---| +| `bin/hugo-build` | Production/dev build. Sets `HUGO_ENVIRONMENT` so `postcss.config.js` can skip PurgeCSS/cssnano in dev mode. Runs `bin/validate-course` first if present. | +| `bin/rake test:critical` | Runs `test/system/*_test.rb` — the fast required gate after any change. | +| `bin/test` / `bin/dtest` | Visual regression on macOS host / Linux Docker respectively — see [visual-scroll-gate](/workflows/visual-scroll-gate.md) and [testing](/workflows/testing.md). | + +# Citations + +[1] `bin/hugo-build` +[2] `config/_default/hugo.toml` +[3] `Rakefile` diff --git a/.okf/architecture/index.md b/.okf/architecture/index.md new file mode 100644 index 000000000..c3e0e4ef4 --- /dev/null +++ b/.okf/architecture/index.md @@ -0,0 +1,7 @@ +# Architecture + +* [Hugo Site](hugo-site.md) - Hugo static site setup, theme, build/test commands +* [CSS Build Pipeline](css-pipeline.md) - PostCSS + per-bundle PurgeCSS, FL-Builder legacy CSS +* [Blog Index / Listing Page](blog-list-page.md) - blog/list.html template, blog-list CSS bundle +* [Blog Cover Image Pipeline](cover-image-pipeline.md) - JetVelocity cover generation, og:image vs thumbnail rendering +* [Enhanced SEO Meta Tags](seo-meta-tags.md) - per-section title/description generation partial diff --git a/.okf/architecture/seo-meta-tags.md b/.okf/architecture/seo-meta-tags.md new file mode 100644 index 000000000..79c97bbce --- /dev/null +++ b/.okf/architecture/seo-meta-tags.md @@ -0,0 +1,24 @@ +--- +type: Service +title: Enhanced SEO Meta Tags +description: Hugo partial that generates optimized page titles, meta descriptions, and schema tags per section with emotional-trigger power words. +resource: themes/beaver/layouts/partials/seo/enhanced-meta-tags.html +tags: [seo, hugo, meta-tags] +timestamp: 2026-07-12T00:00:00Z +--- + +# Overview + +`partials/seo/enhanced-meta-tags.html` generates the page `` and +meta description per section (home, services, about, blog, default), +enforcing length limits (title 50-60 chars, description 150-160 chars) +and injecting a per-section "power word" (e.g. "Expert", "Proven", +"Essential", "Advanced") plus a fixed `| JetThoughts` suffix. + +This is distinct from the per-post `og:image`/`twitter:image` tags, +which come from [cover-image-pipeline](/architecture/cover-image-pipeline.md) +via `page/cover_image.html`. + +# Citations + +[1] `themes/beaver/layouts/partials/seo/enhanced-meta-tags.html` diff --git a/.okf/content-strategy/icp-primary-website-target.md b/.okf/content-strategy/icp-primary-website-target.md new file mode 100644 index 000000000..16b6815d4 --- /dev/null +++ b/.okf/content-strategy/icp-primary-website-target.md @@ -0,0 +1,31 @@ +--- +type: Reference +title: "ICP-E: Non-Technical Founder Hiring a Devshop" +description: Primary website lead-gen ICP profile — "Alex," a burned non-technical founder — used to gate all content, blog, design, and marketing work. +resource: docs/90-99-content-strategy/strategy-analysis/90.10-icp-primary-website-target.md +tags: [icp, content-strategy, marketing] +timestamp: 2026-05-08T00:00:00Z +--- + +# Overview + +ICP-E ("Alex, the Burned Non-Technical Founder") is the primary website +lead-generation target, chosen over technical-buyer ICPs for: critical +pain intensity (already burned by a prior devshop), high budget +(funded startups willing to pay to fix a disaster), fast decision speed +(1-2 weeks, founder decides alone), low competition (most devshops +target technical buyers), and the 2026 AI-generated-code-debt +("vibe coding") trend. + +Profile: age 30-45 primary (45-60 secondary), non-technical, has been +burned by a prior development engagement. + +This ICP must be read before any content, blog, design, or landing-page +work — it is a mandatory read per project instructions. It is distinct +from the **course ICP** ("Sam," an idea-stage non-technical first-timer) +used for `docs/projects/2605-tech-for-non-technical-founders/` — rescue/ +trauma framing that fits Alex is off-tone for Sam-facing course bodies. + +# Citations + +[1] `docs/90-99-content-strategy/strategy-analysis/90.10-icp-primary-website-target.md` diff --git a/.okf/content-strategy/index.md b/.okf/content-strategy/index.md new file mode 100644 index 000000000..23765fa67 --- /dev/null +++ b/.okf/content-strategy/index.md @@ -0,0 +1,4 @@ +# Content Strategy + +* [ICP-E: Non-Technical Founder](icp-primary-website-target.md) - primary website lead-gen target profile ("Alex") +* [Voice Guide](voice-guide.md) - canonical voice/tone/banned-pattern reference for founder-facing content diff --git a/.okf/content-strategy/voice-guide.md b/.okf/content-strategy/voice-guide.md new file mode 100644 index 000000000..29e2dcbd9 --- /dev/null +++ b/.okf/content-strategy/voice-guide.md @@ -0,0 +1,31 @@ +--- +type: Reference +title: JetThoughts Voice Guide — ICP-E Content +description: Canonical voice reference for all founder-facing content (blog, landing pages, emails, social) — tone spectrum, banned words, and anti-AI structural patterns. +resource: docs/90-99-content-strategy/strategy-analysis/90.11-voice-guide.md +tags: [voice, content-strategy, writing] +timestamp: 2026-04-11T00:00:00Z +--- + +# Overview + +Voice formula: Rob Walling's directness (70%) + Rand Fishkin's +vulnerability (30%). One-sentence summary: write like a founder who's +rescued 50 projects talking to another founder at a bar — not a +consulting firm publishing a whitepaper. + +Tone spectrum: direct/blunt/specific (not vague/hedging/diplomatic); +empathetic — "we've seen this before" (not sympathetic — "poor you"); +peer-to-peer (not vendor-to-prospect); self-aware (not infallible); +conversational with contractions and fragments (not formal); opinionated +(not neutral-hedging). + +This guide, along with [icp](/content-strategy/icp-primary-website-target.md), +must be read before writing any blog post or marketing copy, and +overrides generic writing/SEO/humanizer skill advice on conflict. See +[blog-pipeline](/workflows/blog-pipeline.md) for the zero-tolerance +AI-pattern list and self-test checklist this guide defines. + +# Citations + +[1] `docs/90-99-content-strategy/strategy-analysis/90.11-voice-guide.md` diff --git a/.okf/index.md b/.okf/index.md index 091b515fb..c84993500 100644 --- a/.okf/index.md +++ b/.okf/index.md @@ -13,4 +13,6 @@ Curated operational knowledge for the Hugo static site at * [Build & Test](build/) - build pipeline, validators, and the blocking test gates * [Content](content/) - course structure, canonical numbers, and voice rules * [Design](design/) - mermaid theme, house visual spec, covers, typography -* [Workflows](workflows/) - render-verification recipes and review-swarm patterns +* [Workflows](workflows/) - render-verification recipes, review-swarm patterns, and the blog/LinkedIn/CSS/test pipelines +* [Architecture](architecture/) - Hugo site, CSS build pipeline, blog templates, cover images, SEO meta tags +* [Content Strategy](content-strategy/) - ICP profile and voice guide governing all founder-facing content diff --git a/.okf/log.md b/.okf/log.md index bab56abdd..87e376737 100644 --- a/.okf/log.md +++ b/.okf/log.md @@ -16,6 +16,18 @@ exceed the band - EXEMPLAR PARITY on an identical measurement basis is the real gate, not the raw number. M3 (3.1/3.2 at ~2,900w) is the remaining unmigrated pair. +## 2026-07-18 - Merged blog/site bundle into course bundle +Folded a separately-produced Hugo blog/site OKF bundle into this one as two +new sibling sections: `architecture/` (hugo-site, css-pipeline, blog-list-page, +cover-image-pipeline, seo-meta-tags) and `content-strategy/` +(icp-primary-website-target, voice-guide). Added five site/content workflow +concepts to `workflows/` alongside the existing course pair: blog-pipeline, +linkedin-post-pipeline, css-maintainability-plan, visual-scroll-gate, testing. +Root index and workflows index updated with the new entries. No course-side +concepts changed; distilled from CLAUDE.md, bin/hugo-build, Rakefile, +config/_default/hugo.toml, theme layouts, and docs/workflows/ + +docs/90-99-content-strategy/. + ## 2026-07-17 (later) - Full-course migration complete M3 (3.1/3.2), M4 (all five), and M5 (5.1/5.2/5.6/5.7) migrated to v2 micro-lesson form in one 5-fixer wave; 11 new reference companions created diff --git a/.okf/workflows/blog-pipeline.md b/.okf/workflows/blog-pipeline.md new file mode 100644 index 000000000..8fdf689ac --- /dev/null +++ b/.okf/workflows/blog-pipeline.md @@ -0,0 +1,62 @@ +--- +type: Playbook +title: Blog Post Pipeline (Idea to Published) +description: Canonical end-to-end workflow for drafting, reviewing, and publishing a JetThoughts blog post — mandatory for any write/draft/schedule/publish request. +resource: docs/workflows/blog-pipeline.md +tags: [content, blog, workflow] +timestamp: 2026-07-12T00:00:00Z +--- + +# Overview + +`docs/workflows/blog-pipeline.md` is the canonical, mandatory workflow +whenever asked to write, draft, schedule, or publish a blog post — it +must be executed end-to-end, even for `draft: true` posts (so the post +is publish-ready when flipped to `false`). + +# Steps + +1. **Pick topic** — from the active content plan + (`docs/projects/2510-seo-content-strategy/20-29-strategy/20.07-content-plan-icp-e-q2-2026.md`), + next 🔲 Planned topic. +2. **Read context (mandatory)** — [icp](/content-strategy/icp-primary-website-target.md), + [voice-guide](/content-strategy/voice-guide.md), the topic's content-plan + brief, and `.stitch/design.md` for the cover image. +3. **Research** — 5+ authoritative sources; check `content/blog/` for + duplication; every statistic needs a citable source. +4. **Find internal posts to reference (mandatory)** — 4+ verified + internal links via claude-context MCP search, cross-checked against + `docs/blog-post-index.md` (584 posts, 135 tags). Never guess slugs — + verify with `ls content/blog/<slug>/index.md`. +5. **Draft** — `content/blog/<slug>/index.md`, frontmatter constraints + (title ≤45 chars, description 150-160 chars, `cover_image: cover.png`), + structure: hook → primary keyword in first 100 words → numbered H2s → + vibe-coding angle → "when NOT to" → JetThoughts proof point → CTA → + related reading → 5+ external citations. +6. Cover image generation, SEO validation, `bin/hugo-build`, and the + [visual-scroll-gate](/workflows/visual-scroll-gate.md) all run before + the post is considered done — the pipeline does not stop after the + draft step. + +# Zero-tolerance AI patterns + +Rejected on sight: rule of three, signposting, bold inline-header +lists, negative parallelism, triple rhetorical questions, slogany +closings, therapist voice, staccato fragment stacking, noun stacking +without a human subject, telling instead of showing, apologetic +caveats, fabricated timelines/stats. Full list + fixes in the +[voice-guide](/content-strategy/voice-guide.md). + +# Cross-post repetition gates (blocking for cluster posts) + +Before finalizing a post in a topic cluster: (1) **anecdote scan** — no +sibling post may reuse the same named client/incident/dollar figure; +(2) **proof-signal scan** — each post's primary diagnostic signal +(staging URL, test coverage, commit size, secrets storage, rollback +drill, user-impact verification, contract clauses) must be distinct +from its siblings'. + +# Citations + +[1] `docs/workflows/blog-pipeline.md` +[2] `docs/blog-post-index.md` diff --git a/.okf/workflows/css-maintainability-plan.md b/.okf/workflows/css-maintainability-plan.md new file mode 100644 index 000000000..55c483a01 --- /dev/null +++ b/.okf/workflows/css-maintainability-plan.md @@ -0,0 +1,48 @@ +--- +type: Decision +title: CSS Maintainability Redesign (approved 2026-07-12) +description: Approved plan superseding the original CSS-duplication-elimination goal; targets hand-editable, single-source CSS and FL-Builder retirement instead of raw line-count reduction. +resource: docs/projects/2509-css-migration/2026-07-12-css-maintainability-redesign.md +tags: [css, decision, migration] +timestamp: 2026-07-12T00:00:00Z +--- + +# Overview + +Approved by Paul on 2026-07-12, superseding Phase 3 (WP3.1–3.4) of the +original CSS migration plan. The original goal — "eliminate 70-80% CSS +duplication (27,094–31,536 lines)" — was falsified twice: because +PurgeCSS runs per bundle (see [css-pipeline](/architecture/css-pipeline.md)), +each page already ships only its own subset, and consolidating source +files can *grow* shipped bytes. Source line counts predict nothing +about shipped bytes. + +# The real problem + +Named by Paul: **fear of cross-page breakage** — nobody can tell which +pages a CSS edit affects, and ~75K lines of generated FL-Builder export +CSS (`.fl-node-*` selectors keyed to hash IDs) are effectively +obfuscated and un-editable. + +# Goal & success criteria + +Every style is hand-editable, understood, and single-source. + +1. FL retirement burn-down: live FL layout files 16 → 0. Each rewritten + page deletes its FL CSS file(s) and template's `fl-node-*` markup in + the same PR. +2. Zero obfuscated artifacts at end state (no hash-named CSS files, no + generated node-id rules). +3. No duplication in the hand-maintained layer — new CSS uses tokens + from `foundations/css-variables.css` and shared components. +4. Safe-edit guarantee — "which pages does this affect?" answerable in + under a minute via the ownership map + (`docs/projects/2509-css-migration/css-bundle-ownership-map.md`); + `bin/test` + `bin/dtest` verify changes end-to-end. +5. **Evidence rule**: any size/perf claim is validated on compiled + + gzip per-page payload, never raw source line counts. + +# Citations + +[1] `docs/projects/2509-css-migration/2026-07-12-css-maintainability-redesign.md` +[2] `docs/projects/2509-css-migration/css-bundle-ownership-map.md` diff --git a/.okf/workflows/index.md b/.okf/workflows/index.md index de645119b..421f44659 100644 --- a/.okf/workflows/index.md +++ b/.okf/workflows/index.md @@ -2,3 +2,8 @@ * [Render verification](render-verification.md) - headless Chrome + slicing recipes for the visual scroll gate * [Review swarm](review-swarm.md) - the two-critic review pattern and its failure modes +* [Blog Post Pipeline](blog-pipeline.md) - mandatory end-to-end workflow for writing/publishing blog posts +* [LinkedIn Post Pipeline](linkedin-post-pipeline.md) - Paul Keen voice rules and posting workflow +* [CSS Maintainability Redesign](css-maintainability-plan.md) - approved plan for hand-editable CSS + FL-Builder retirement +* [Visual Scroll Gate](visual-scroll-gate.md) - blocking pre-handback visual walk for content/visual changes +* [Test Suite & Gates](testing.md) - Minitest structure, rake tasks, visual regression gate diff --git a/.okf/workflows/linkedin-post-pipeline.md b/.okf/workflows/linkedin-post-pipeline.md new file mode 100644 index 000000000..080cf3665 --- /dev/null +++ b/.okf/workflows/linkedin-post-pipeline.md @@ -0,0 +1,41 @@ +--- +type: Playbook +title: LinkedIn Post Pipeline — Paul Keen / JetThoughts +description: Canonical voice reference and workflow for writing LinkedIn posts under Paul Keen's personal account, primarily for the ICP-E validation campaign. +resource: docs/workflows/linkedin-post-pipeline.md +tags: [content, linkedin, workflow, voice] +timestamp: 2026-07-12T00:00:00Z +--- + +# Overview + +All LinkedIn posts (`linkedin-posts/**/*.md`) are personal, authored by +Paul Keen from his own profile — never corporate JetThoughts marketing. +First-person voice only ("I"/"me"/"my"), never "we"/corporate plural. + +This file overrides the blog voice guide when the two conflict, for +LinkedIn drafts specifically. + +# Mandatory pre-writing reads + +1. `docs/workflows/linkedin-icp-validation-plan.md` — campaign + hypotheses, content pillars, weekly plan. +2. [icp](/content-strategy/icp-primary-website-target.md) — ICP-E + definition, pain language, control-loss patterns. +3. [voice-guide](/content-strategy/voice-guide.md) — voice formula, + banned words, anti-AI structural patterns. +4. This file — LinkedIn-specific overrides. + +# Blocking self-critique requirement + +Every draft or revision of a LinkedIn post must invoke the +`reflexion-reflect` skill (Standard Path) BEFORE first handback. After +any user pushback on the same draft, escalate to `reflexion-critique` +(multi-judge debate). Solo iteration past first delivery has +repeatedly missed pattern-level tells (over-narration, cinematic +beat-marking, shape-coded copywriting). + +# Citations + +[1] `docs/workflows/linkedin-post-pipeline.md` +[2] `docs/workflows/linkedin-icp-validation-plan.md` diff --git a/.okf/workflows/testing.md b/.okf/workflows/testing.md new file mode 100644 index 000000000..fa1b841b7 --- /dev/null +++ b/.okf/workflows/testing.md @@ -0,0 +1,48 @@ +--- +type: Playbook +title: Test Suite & Gates +description: Minitest-based test suite structure and the mandatory test/visual-regression gates for HTML/CSS/template changes. +resource: Rakefile +tags: [testing, minitest, ci] +timestamp: 2026-07-12T00:00:00Z +--- + +# Overview + +Tests use Minitest under `test/`, driven by `Rakefile` (`Rake::TestTask`). +`test/test_helper.rb` sets `SYNC_ENV=test`, a fake `DEVTO_API_KEY`, and +loads `lib/` onto `$LOAD_PATH`. + +| Rake task | Pattern | Use | +|---|---|---| +| `rake test` / `rake test:all` | `test/**/*_test.rb` | Full suite | +| `rake test:unit` | `test/unit/**/*_test.rb` | Unit tests | +| `rake test:critical` | `test/system/*_test.rb` | Fast required gate after every micro-change (<10 lines) | +| `rake test:integration` | `test/integration/**/*_test.rb` | Integration tests | +| `rake build` | — | Runs `bin/hugo-build` | +| `rake dev` | — | Runs `bin/hugo-dev` | +| `rake lighthouse[limit]` | — | Runs `bin/lighthouse` across crawled pages | + +Default rake task is `test:all`. + +# Visual regression gate (mandatory for CSS/HTML/template changes) + +After ANY edit to `themes/`, `layouts/`, blog post body HTML, or +`*.css`, run BOTH `bin/test` (macOS host) and `bin/dtest` +(Linux/Docker) before committing — they write to separate baseline +directories (`test/fixtures/screenshots/macos/` vs `linux/`) and CI +runs Linux, so `bin/test` alone can pass locally while shipping a +broken Linux build. Tolerance: 0.0 for refactors (zero visual +difference), ≤0.03 for genuinely new features. Failures are commit +blockers, not warnings — either fix the regression or confirm intent +and update both baseline directories in the same commit. + +Test quality rule: assertions must be behavior-focused, not +config/existence checks. Don't hardcode tunable values (`q=90`, `w=360`, +exact byte sizes) — assert shape (`q=\d+`, presence of `<picture>`, +`src` contains `wsrv.nl`) instead. + +# Citations + +[1] `Rakefile` +[2] `test/test_helper.rb` diff --git a/.okf/workflows/visual-scroll-gate.md b/.okf/workflows/visual-scroll-gate.md new file mode 100644 index 000000000..44265859a --- /dev/null +++ b/.okf/workflows/visual-scroll-gate.md @@ -0,0 +1,53 @@ +--- +type: Playbook +title: Visual Scroll Gate +description: Blocking pre-handback protocol requiring a full section-by-section visual walk of any new or edited content page at desktop and mobile viewports. +resource: docs/workflows/visual-scroll-gate.md +tags: [testing, visual-regression, chrome-devtools] +timestamp: 2026-07-12T00:00:00Z +--- + +# Overview + +Applies to any new/edited content page (course lesson, walkthrough, +companion/template page, blog post, landing page) and any change to a +visual asset (SVG, mermaid diagram, cover image). Must run BEFORE +declaring the page done, in the same session as the edit. Text +validators cannot see rendered output — this gate is the substitute for +human eyes. + +On 2026-07-10 a single Module 3 pass caught: mermaid node text clipped +in every node, SVG text crossing an artwork's card border, a stale +"Curriculum 06/30" badge on a cover freshly wired as `og:image`, an +"above" image reference that actually rendered below, and a chapter +renumber leftover living inside SVG artwork (invisible to any +banned-string text ratchet). + +# Protocol + +1. `hugo server` + chrome-devtools MCP. Load the page at 1280×800. +2. Scroll section by section (every h2 anchor, every diagram/image/ + table/callout) and screenshot each view — actually look at each + screenshot, "screenshot taken" is not the gate. +3. Repeat at 390×844 (minimum: numeric probes + screenshots of every + diagram). +4. Run numeric probes: `bodyOverflowX`, mermaid SVG rendered heights, + per-image `complete`/height. +5. Check console errors and network 404s. Dev-server-only artifacts are + excusable if named explicitly; everything else blocks. + +# Related content-quality gates + +- **Cognitive load / F-pattern rules** for long-form posts (>800 + words): first-3-seconds visual hook, no 6+ identical bullets/table + rows, decision-aid format (flowchart/table) for "if X then Y" + sections, labels inside diagrams (not beside), one visual break per + H2, no decorative visuals. +- **Visual verification gate** for any new media element: score + against look/functional/scroll-earning/helpfulness; any NO/MIXED on + the latter two = rollback or redesign before commit. + +# Citations + +[1] `docs/workflows/visual-scroll-gate.md` +[2] `docs/projects/2605-tech-for-non-technical-founders/10-19-research/10.05-content-organization-patterns-2026.md` From a768b8a91f42159a55fe39dfd5a413b0dd7a1cfe Mon Sep 17 00:00:00 2001 From: Paul Keen <pftg@Mac.fritz.box> Date: Sat, 18 Jul 2026 22:48:41 +0200 Subject: [PATCH 2/2] docs(okf): correct seo/cover concepts to match implemented behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address CodeRabbit review on #366: - seo-meta-tags: describe actual behavior — per-branch hardcoded titles, max-length caps (60/160, not guaranteed ranges), varying suffixes (| JetThoughts / | JT / | JetThoughts Blog / none), and note the $powerWords dict + $titleSuffix var are defined but unused. Add the partial's own 1200x630 og:image path. - cover-image-pipeline: attribute the 1200x630 / 640·960·1920 / 360x189 sizes to the .stitch/design.md spec as target output, not template derivation; point the fixed 1200x630 og:image at seo-meta-tags. - log: move the 2026-07-18 entry below both 2026-07-17 entries to keep the chronological order. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --- .okf/architecture/cover-image-pipeline.md | 12 ++++++--- .okf/architecture/seo-meta-tags.md | 31 ++++++++++++++++------- .okf/log.md | 24 +++++++++--------- 3 files changed, 42 insertions(+), 25 deletions(-) diff --git a/.okf/architecture/cover-image-pipeline.md b/.okf/architecture/cover-image-pipeline.md index d7de70190..983eda904 100644 --- a/.okf/architecture/cover-image-pipeline.md +++ b/.okf/architecture/cover-image-pipeline.md @@ -12,16 +12,20 @@ Cover images are generated at a 2400×1260 source PNG (2x retina) using the **JetVelocity "Obsidian Engine"** design system defined in `.stitch/design.md`, and follow a mandatory 6-slot layout (brand/category, year pill, 3-line gradient headline, visual ember, tag chips, status -chip). Hugo derives the 1200×630 `og:image`, responsive -640/960/1920 WebP/JPG variants, and a 360×189 thumbnail from that -source at build/CDN time. +chip). The 1200×630 og:image / 640·960·1920 responsive variants / +360×189 thumbnail listed in `.stitch/design.md` are the design spec's +**target** output sizes; the actual derivation each template performs is +described below — the source PNG is what the pipeline is authored +against. Two distinct rendering paths consume the same source image: - **`page/cover_image.html`** — emits only the `og:image` / `twitter:image` meta tags. If `site.Params.cdn.enabled`, it uses the CDN-resized resource's own `Permalink`/`Width`/`Height` directly; - otherwise it falls back to a local Hugo `.Fit "512x512 jpeg"`. + otherwise it falls back to a local Hugo `.Fit "512x512 jpeg"`. (The + fixed 1200×630 og:image is emitted separately by + [seo-meta-tags](/architecture/seo-meta-tags.md), not here.) - **`partials/blog/img-cropped.html`** — renders the actual `<picture>` thumbnail markup used on cards/list pages. It resolves the source image from `metatags.image` (primary convention), falling diff --git a/.okf/architecture/seo-meta-tags.md b/.okf/architecture/seo-meta-tags.md index 79c97bbce..e57da43a6 100644 --- a/.okf/architecture/seo-meta-tags.md +++ b/.okf/architecture/seo-meta-tags.md @@ -1,7 +1,7 @@ --- type: Service title: Enhanced SEO Meta Tags -description: Hugo partial that generates optimized page titles, meta descriptions, and schema tags per section with emotional-trigger power words. +description: Hugo partial that generates per-section page titles, meta descriptions, robots/canonical/OpenGraph/Twitter tags, and its own resized og:image. resource: themes/beaver/layouts/partials/seo/enhanced-meta-tags.html tags: [seo, hugo, meta-tags] timestamp: 2026-07-12T00:00:00Z @@ -9,15 +9,28 @@ timestamp: 2026-07-12T00:00:00Z # Overview -`partials/seo/enhanced-meta-tags.html` generates the page `<title>` and -meta description per section (home, services, about, blog, default), -enforcing length limits (title 50-60 chars, description 150-160 chars) -and injecting a per-section "power word" (e.g. "Expert", "Proven", -"Essential", "Advanced") plus a fixed `| JetThoughts` suffix. +`partials/seo/enhanced-meta-tags.html` builds the page `<title>` and +meta description from hardcoded strings chosen per branch (home, +services section vs single service, blog section vs single post, About/ +Contact special cases, generic pages). Emotive words ("Expert", +"Proven", …) are baked into those literal strings — the `$powerWords` +dict and `$titleSuffix` variables it defines are **not actually +referenced** by the title logic. Suffixes therefore vary by branch: +`| JetThoughts` (home, generic pages), `| JT` (blog section index), +`| JetThoughts Blog` (individual posts), or none (About/Contact). -This is distinct from the per-post `og:image`/`twitter:image` tags, -which come from [cover-image-pipeline](/architecture/cover-image-pipeline.md) -via `page/cover_image.html`. +Lengths are **capped, not guaranteed ranges**: the title is truncated +with `…` only if it exceeds 60 chars (`$maxTitleLength`), the +description only if it exceeds 160 chars (`$maxDescLength`) — there is +no minimum-length enforcement. + +The partial also emits robots, canonical (with a `/tags/` → +`/blog/tags/` rewrite for the tag taxonomy), keywords, OpenGraph +(`og:type = article` for single blog pages), Twitter cards, and its +**own** `og:image` resized to 1200×630 (CDN `w=1200&h=630` or a local +`.Resize "1200x630 webp q85"`), with a site-default fallback. This is a +separate og:image path from the one in +[cover-image-pipeline](/architecture/cover-image-pipeline.md). # Citations diff --git a/.okf/log.md b/.okf/log.md index 87e376737..0e1dcf1ce 100644 --- a/.okf/log.md +++ b/.okf/log.md @@ -16,18 +16,6 @@ exceed the band - EXEMPLAR PARITY on an identical measurement basis is the real gate, not the raw number. M3 (3.1/3.2 at ~2,900w) is the remaining unmigrated pair. -## 2026-07-18 - Merged blog/site bundle into course bundle -Folded a separately-produced Hugo blog/site OKF bundle into this one as two -new sibling sections: `architecture/` (hugo-site, css-pipeline, blog-list-page, -cover-image-pipeline, seo-meta-tags) and `content-strategy/` -(icp-primary-website-target, voice-guide). Added five site/content workflow -concepts to `workflows/` alongside the existing course pair: blog-pipeline, -linkedin-post-pipeline, css-maintainability-plan, visual-scroll-gate, testing. -Root index and workflows index updated with the new entries. No course-side -concepts changed; distilled from CLAUDE.md, bin/hugo-build, Rakefile, -config/_default/hugo.toml, theme layouts, and docs/workflows/ + -docs/90-99-content-strategy/. - ## 2026-07-17 (later) - Full-course migration complete M3 (3.1/3.2), M4 (all five), and M5 (5.1/5.2/5.6/5.7) migrated to v2 micro-lesson form in one 5-fixer wave; 11 new reference companions created @@ -42,3 +30,15 @@ Voice wave (same day): 16-page opener-shape rotation shipped - max 4 pages per shape, 4 composite-disclaimed vignettes kept, attribution phrases ("we picked up"/"we worked with") now zero course-wide. Teaching numbers preserved as mechanism descriptions. + +## 2026-07-18 - Merged blog/site bundle into course bundle +Folded a separately-produced Hugo blog/site OKF bundle into this one as two +new sibling sections: `architecture/` (hugo-site, css-pipeline, blog-list-page, +cover-image-pipeline, seo-meta-tags) and `content-strategy/` +(icp-primary-website-target, voice-guide). Added five site/content workflow +concepts to `workflows/` alongside the existing course pair: blog-pipeline, +linkedin-post-pipeline, css-maintainability-plan, visual-scroll-gate, testing. +Root index and workflows index updated with the new entries. No course-side +concepts changed; distilled from CLAUDE.md, bin/hugo-build, Rakefile, +config/_default/hugo.toml, theme layouts, and docs/workflows/ + +docs/90-99-content-strategy/.