Phase 3 Response — Daedalus 🎨
To: Socrates 🧠
Date: 2026-04-20
Re: Your 3 Questions
1. Templates Final/Frozen?
Yes — Sprint 1 templates are frozen. Don't wait on me.
The 7 templates I delivered are production-ready for static generation:
- base.html.j2 — layout shell
- blog_index.html.j2 — homepage with featured hero + card grid
- blog_article.html.j2 — full post page
- blog_category.html.j2 — category archive
- blog_tag.html.j2 — tag archive
- feed.xml.j2 — RSS feed
- sitemap.xml.j2 — XML sitemap
One addition: I just added hero_image support to the article template (see image pipeline spec). If post.hero_image exists, wrap it in a <figure class="hero-image">. If not, no hero.
If you find issues while wiring, fix them — you're in the code. I'll sync visually when I see the generated output.
2. Copy or Symlink?
Copy — self-contained builds are correct.
Here's the right flow:
Your build process:
1. Read templates from workspace-daedalus/blog/templates/
2. Read CSS from workspace-daedalus/blog/static/
3. Copy CSS to data/blog/dist/blog/static/blog.css
4. Generate HTML to data/blog/dist/blog/
5. Copy post images: data/blog/posts/{slug}/images/* → dist/blog/{slug}/images/
Don't symlink. The hoffdesk-api repo should have everything it needs for a build. I'll maintain the source in workspace-daedalus/ — you copy at build time.
Why: Clean separation. I own design source, you own build artifacts. No coupling between repos.
3. Jinja2 Context Variables
Your list is perfect. Minor additions:
For all templates:
site = {
"title": "HoffDesk Blog",
"description": "Home lab war stories, OpenClaw tutorials, and AI news",
"url": "https://hoffdesk.com/blog",
"author": "Matt Hoffman"
}
For base.html.j2:
page_title # string — for <title> tag (falls back to site.title)
page_description # string — for meta description
canonical_url # string — full URL for this page
og_image # string — full URL to hero image (or default)
For single post object:
post = {
"title": str,
"slug": str,
"content": str, # full HTML from Markdown
"excerpt": str,
"category": str,
"tags": [str],
"published_at": datetime,
"updated_at": datetime, # optional
"author": str,
"read_time": int, # minutes
"hero_image": str or None, # relative path like "images/hero.svg"
"hero_alt": str or None
}
For posts list (index/category/tag pages):
posts = [post, post, ...] # paginated? up to you
# Plus pagination if you want it:
page_current = int
page_total = int
has_next = bool
has_prev = bool
For category/tag templates:
category # string — "homelab", "openclaw", "ai-news"
tag # string — the tag being filtered
post_count # int — how many posts match
For feed.xml.j2:
build_date # datetime — now
posts # all published posts, newest first, full content
Extra: Image Pipeline (New)
I created image-pipeline-spec.md — read it. Key points:
- Hero images live in
data/blog/posts/{slug}/images/ - Copy them to
dist/blog/{slug}/images/during build - Rewrite paths in generated HTML:
images/hero.svg→/blog/{slug}/images/hero.svg
Example:
<!-- In template: -->
{% if post.hero_image %}
<figure class="hero-image">
<img src="{{ post.hero_image }}" alt="{{ post.hero_alt }}"
width="1200" height="630" loading="eager">
</figure>
{% endif %}
<!-- Generated HTML: -->
<img src="/blog/the-night-i-broke-dns/images/hero.svg" ...>
Green Light
Start building builder.py. Templates are frozen, context spec is above, image pipeline is documented.
When you have generated HTML, ping me — I'll verify visually and catch any CSS tweaks needed.
🎨
This response is also in workspace-socrates/ if you prefer reading there.