Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .okf/architecture/blog-list-page.md
Original file line number Diff line number Diff line change
@@ -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 `<picture>`
srcsets, cover meta tags need only a single canonical URL.

# Citations

[1] `themes/beaver/layouts/blog/list.html`
50 changes: 50 additions & 0 deletions .okf/architecture/cover-image-pipeline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
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). 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"`. (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
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`
50 changes: 50 additions & 0 deletions .okf/architecture/css-pipeline.md
Original file line number Diff line number Diff line change
@@ -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)
44 changes: 44 additions & 0 deletions .okf/architecture/hugo-site.md
Original file line number Diff line number Diff line change
@@ -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/<slug>/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`
7 changes: 7 additions & 0 deletions .okf/architecture/index.md
Original file line number Diff line number Diff line change
@@ -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
37 changes: 37 additions & 0 deletions .okf/architecture/seo-meta-tags.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
type: Service
title: Enhanced SEO Meta Tags
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
---

# Overview

`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).

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

[1] `themes/beaver/layouts/partials/seo/enhanced-meta-tags.html`
31 changes: 31 additions & 0 deletions .okf/content-strategy/icp-primary-website-target.md
Original file line number Diff line number Diff line change
@@ -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`
4 changes: 4 additions & 0 deletions .okf/content-strategy/index.md
Original file line number Diff line number Diff line change
@@ -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
31 changes: 31 additions & 0 deletions .okf/content-strategy/voice-guide.md
Original file line number Diff line number Diff line change
@@ -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`
4 changes: 3 additions & 1 deletion .okf/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
12 changes: 12 additions & 0 deletions .okf/log.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,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/.
62 changes: 62 additions & 0 deletions .okf/workflows/blog-pipeline.md
Original file line number Diff line number Diff line change
@@ -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`
Loading
Loading