The social-share system from entry 019 only ever covered posts. Nobody had noticed the gap on project and chapter pages until a completely unrelated question — while looking at a different workshop project’s own page — asked it directly: where’s the OG image for the project itself? There wasn’t one; it fell back to the generic site tagline.

The fix generalized the existing renderer instead of patching one page: two new endpoints, keyed off real project and chapter data the same way the post endpoint is keyed off post data. Because it was built generic rather than hardcoded, this series’ own project page and all seven of its chapters picked up real OG images the moment it shipped, with no separate work required.

That only fixed what a link preview shows, though — nothing on the site itself displayed the images anywhere. The second half put them to use: thumbnails on every card shaped like a box (the Hub, the workshop project list, prev/next navigation), left untouched on the cards shaped like a shared list of rows, since those would need a real redesign rather than one <img> tag.

Main takeaways

  • A gap noticed sideways, while working on something else entirely, is still worth fixing generally instead of just where it was spotted.
  • Build a mechanism generically once and every current and future consumer of it benefits automatically — no follow-up work needed on the sibling case.
  • Not every card shape can take an image cheaply. Knowing which ones can — and deliberately leaving the others alone — beats forcing a redesign to satisfy a request literally.

What I got wrong

  • No TypeScript checker is installed in this project, so the two new endpoints only got runtime verification (curl, a rendered meta tag), not a static safety net.
  • Automated verification looked broken when it wasn’t — loading="lazy" plus a flaky test harness produced false negatives that took real pixel-sampling to disprove. A tool reporting nothing isn’t the same as there being nothing to find.
  • Neither the workshop index nor a project page shows any “last updated” signal — a real, still-open gap this pass didn’t close.