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
2 changes: 1 addition & 1 deletion .okf/architecture/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* [CSS Build Pipeline](css-pipeline.md) - PostCSS + per-bundle PurgeCSS, the site-wide token layer and its zero-delta promotion pattern, FL-Builder legacy CSS
* [Blog Index / Listing Page](blog-list-page.md) - index AND tag-page templates, the shared row/filter/CTA partials, blog-list CSS bundle, and the term-kind / date-fallback traps
* [Blog Cover Image Pipeline](cover-image-pipeline.md) - JetVelocity cover generation, og:image vs thumbnail rendering, and the responsive mobileWidth/mobileSizes params list covers need
* [Enhanced SEO Meta Tags](seo-meta-tags.md) - per-section title/description generation partial
* [Enhanced SEO Meta Tags](seo-meta-tags.md) - per-section title/description generation partial, its own og:image path, and the site-default social fallback
* [ICP Journey SIPOC](icp-journey-sipoc.md) - pointer: Mermaid flowchart of Sam's full journey through every lesson, gate, branch, and artifact
* [Operational Dependency Map](operational-dependency-map.md) - pointer: per-lesson prerequisites, template blank sources, wait times, Plan B paths

Expand Down
53 changes: 48 additions & 5 deletions .okf/architecture/seo-meta-tags.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
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
resource: layouts/partials/seo/enhanced-meta-tags.html
tags: [seo, hugo, meta-tags]
generated:
by: process:okf-migrate
at: 2026-07-12T00:00:00Z
verified:
- { by: claude/opus-5, at: 2026-08-20T23:50:00Z }
timestamp: 2026-08-20T23:50:00Z
---

# Overview
Expand All @@ -29,11 +32,51 @@ 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
**own** `og:image` resized to 1200×630 (CDN `w=1200&h=630&output=jpg&q=85`,
or a local `.Resize "1200x630 jpg q90"`), with a site-default fallback. This
is a separate og:image path from the one in
[cover-image-pipeline](/architecture/cover-image-pipeline.md).

**JPEG is deliberate, not incidental**: LinkedIn's crawler will not render
WebP, so an og:image emitted as WebP unfurls blank there even though most
other clients handle it.

# The site-default fallback

When no frontmatter image field resolves, the partial falls back to a
hardcoded `https://jetthoughts.com/assets/images/og-default.jpg`. **This
asset went missing for an unknown period and was only added 2026-08-20** -
553 pages, overwhelmingly blog posts without cover art, unfurled blank on
LinkedIn and Slack the whole time. `og:image` is the one asset class no
crawler, link-checker, or reader ever exercises, so nothing surfaced it.

It is a brand plate (wordmark + category line + canon proof chips), NOT a
campaign pitch: a fallback stands in under arbitrary technical posts, where
sales copy over a Puma-config article reads as an ad. Any figure on it is
bound by [claims-canon](/content/claims-canon.md), and because those figures
live inside a binary, **no text ratchet can see them** - the generator source
is kept in-repo so they stay greppable:

```
# .stitch/og-default.svg (NOT .stitch/designs/, which is gitignored)
rsvg-convert -w 1200 -h 630 -o /tmp/og.png .stitch/og-default.svg
magick /tmp/og.png -quality 88 static/assets/images/og-default.jpg
```

Verified byte-identical to the committed jpg (`md5
ecd26681b0c591a68d1315d5d578d05c`), so the SVG is the source of record rather
than an approximation of it - a canon change is edited there and re-rendered,
never painted onto the jpg.

Guarded since 2026-08-20 by `test/unit/og_image_resolves_test.rb`, which
resolves every same-origin `og:image`/`twitter:image` in RENDERED output
against the build. Known gap: the two theme partials
(`blog/list.html`, `page/cover_image.html`) guard their tags with
`{{ if . }}` / `{{ with }}`, so a missing resource DROPS the tag rather than
dangling it - a page that loses its social image entirely is invisible to
that gate.

# Citations

[1] `themes/beaver/layouts/partials/seo/enhanced-meta-tags.html`
[1] `layouts/partials/seo/enhanced-meta-tags.html`
[2] `static/assets/images/og-default.jpg` - the site-default fallback asset
14 changes: 13 additions & 1 deletion .okf/build/ci-gates.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,21 @@ Consequences for how to react:
stall anyway (run 32414801788, 10 min on `--depth=1`). The one job that
cannot go shallow is the one that must not: `enableGitInfo` in
`config/_default/hugo.toml` needs commit history to resolve `.Lastmod`.

* **Do not copy `filter: blob:none` into the other workflows.** `_hugo.yml`
is the ONLY job that sets `fetch-depth: 0`, and the filter's whole win is
skipping *historical* blobs. Every other checkout — `test.yml:66`,
`publish.yml:45` and `:82`, `link-check.yml`, `sync-and-publish.yml`,
`process-issue-zip.yml` — runs at the default depth 1, where there is no
history to skip and git must still materialise every blob at HEAD to
populate the working tree. Adding the filter there buys nothing and risks
a slower lazy per-blob fetch. Their 7-minute checkouts are the content
weight below, not a missing flag (established 2026-08-20 while triaging a
checkout-stalled Unit Tests job on PR #511).
* **The deeper fix is the content weight**, not the checkout flags: 625 MB of
images in git is the floor every job pays. Moving them to LFS or
CDN-only would be a separate, larger decision.
CDN-only would be a separate, larger decision — and it is the only lever
left for the depth-1 jobs.

[actions/checkout#2441]: https://github.com/actions/checkout/issues/2441

Expand Down
2 changes: 1 addition & 1 deletion .okf/build/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Build & Test

* [Hugo build pipeline](hugo-build.md) - bin/hugo-build with the 8 course validators; also the PurgeCSS cold-start race and the minified-unquoted-attribute audit-tool trap
* [Test gates](test-gates.md) - the local suites, when each is a commit blocker, and bin/record-baselines for accepting only the baselines you meant to move
* [Test gates](test-gates.md) - the local suites, when each is a commit blocker, bin/record-baselines for accepting only the baselines you meant to move, and why a deleted source file still serves from every local _dest/ tree
* [CI gates](ci-gates.md) - what GitHub Actions enforces: build, unit, path-scoped link check (visual regression is report-only), and what gates a PR never sees
* [Template PDFs](pdf-templates.md) - regenerating the downloadable course PDFs
47 changes: 47 additions & 0 deletions .okf/build/test-gates.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ generated: { by: claude/opus-4-8, at: 2026-08-12T20:20:00Z }
verified:
- { by: claude/fable-5, at: 2026-08-01T11:30:00Z }
- { by: claude/sonnet-5, at: 2026-08-20T00:00:00Z }
- { by: claude/opus-5, at: 2026-08-20T23:45:00Z }
- { by: claude/opus-5, at: 2026-08-21T00:00:00Z }
timestamp: 2026-08-21T00:00:00Z
---
Expand Down Expand Up @@ -216,6 +217,46 @@ Minitest under `test/`, driven by `Rakefile` (`Rake::TestTask`).
a build-recipe change invalidates the tree. Diagnosis tell: `grep
localhost:1314 _dest/<tree>/404.html` returns hits; escape hatch is
`FORCE_BUILD=1` or `rm -rf` the tree.

- **No local build path passes `--cleanDestinationDir`, so DELETED sources
keep serving from the dest tree forever** (2026-08-20). `bin/hugo-build:47`
builds with `hugo build --noBuildLock --environment <env> --destination
<dir>` and `Hugo#precompile` (`test/support/hugo_helpers.rb:24-38`) with a
bare `hugo --destination <dir>`; only the CI Pages build
(`.github/workflows/_hugo.yml:82`) cleans. Hugo does not remove outputs
whose source is gone, so an orphaned page or asset persists in every local
`_dest/` tree until someone `rm -rf`s it. Note this is NOT the staleness
probe failing - `bin/build-if-stale` handles deletions correctly (it
probes DIRECTORIES, whose mtime a delete bumps). The tree is genuinely
rebuilt and STILL serves the deleted file, which is why the usual
"did it rebuild?" reflex diagnoses it wrong.

Consequence for any rendered-output test: **deleting a source file to
prove a test goes RED proves nothing.** On 2026-08-20 a new og:image test
stayed green through three delete-and-rerun attempts against a stale
`_dest/public-test-local`. Clear the dest dir before trusting a RED, the
same way a screenshot baseline must be COMMITTED before trusting a
re-record (both are "the assertion is right, the input is stale").

- **A rendered-output sweep can look thorough and check almost nothing**
(2026-08-20). Count the DISTINCT values a sweep actually resolves before
trusting its breadth - a glob over 1757 files is not coverage. The first
`og_image_resolves_test.rb` matched `property="..." content="..."` as a
FIXED attribute sequence. Two facts collapsed 1297 tag matches to ONE url
checked: most values are off-origin CDN urls that fell through the
same-origin filter, and the theme partials
(`themes/beaver/layouts/blog/list.html:23`,
`themes/beaver/layouts/partials/page/cover_image.html:3,:11`) write
`content=` BEFORE `property=`, so the content-first tag on 480 pages was
dropped. Those pages still matched via their property-first tag, which is
why nothing looked wrong - what was lost was one whole distinct image.
Match whole tags and extract the attribute separately.

The trap underneath: **a reproduction test can be honestly RED for the
right reason and still cover nothing else.** RED->GREEN passed cleanly
because the single path the sweep reached WAS the defect under repair.
Coverage and correctness are independent; proving the second says
nothing about the first.
- The snap_diff HTML report at
`test/fixtures/screenshots/snap_diff_report.html` (gitignored) is written
automatically on any RED run - the gem auto-registers the reporter on
Expand Down Expand Up @@ -271,6 +312,12 @@ Minitest under `test/`, driven by `Rakefile` (`Rake::TestTask`).
the repo - fresh clones had a hooksPath pointing at nothing. Adding any
root dotfile/dot-dir? Check `git check-ignore -v <path>` before assuming
it's tracked.
Same rule bites SUBdirectories of a tracked dot-dir: `.stitch/*.md` is
tracked but `.stitch/designs/` is ignored, so a source file placed there
is invisible (2026-08-20, the og:image plate source). **`git add <path>`
ERRORS on an ignored path; `git add -A` just silently omits it** - which
is the case for staging by explicit path when a commit's value depends on
a specific file actually landing.
- REPORT-ONLY build gates (each flips to blocking once its backlog hits
zero via an env flag): `bin/check-svg-floor` (`SVG_FLOOR_BLOCK=1`)
catches course SVGs whose smallest text renders <9px@390;
Expand Down
5 changes: 4 additions & 1 deletion .okf/design/site-palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,10 @@ anything else going dark is a defect:
# Deprecations in progress

`--color-primary` (`#1a8cff`) is named "primary" and dies in 2608 Phase 1a.2,
along with the late-cascade `#0066d6` anchor rule in 1a.3.
along with the late-cascade `#0066d6` anchor rule in 1a.3. WHEN those phases
run relative to everything else is a separate decision - see the
[rollout sequence](/workflows/site-redesign-rollout.md); the blog bundles ship
before the site-wide chrome, so tokens land in blog CSS first.

**It is the logo's colour, and that is the point, not a reason to keep it.**
`themes/beaver/assets/img/icons/logo-dark.svg` contains exactly one hex value:
Expand Down
6 changes: 5 additions & 1 deletion .okf/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ frontmatter: `generated: { by: <actor>, at: <iso> }`, `verified: [{ by, at }]`,
human-reviewed trust tier. These are OPTIONAL and added honestly by whoever
touches a concept; absence never invalidates it (§11). Legacy concepts still
carrying only `timestamp` remain conformant and migrate as they're edited — do
NOT back-stamp `generated`/`verified` you didn't actually perform.
NOT back-stamp `generated`/`verified` you didn't actually perform. When two
sessions verify the same concept concurrently and a rebase conflicts on the
`verified:` array, keep BOTH entries and take the later `timestamp` - each
verification really happened, and dropping one falsifies the provenance the
field exists to carry (2026-08-20, `build/test-gates.md`).

# Sections

Expand Down
Loading
Loading