Blog — editorial posts (extension)

Strict extension to `tools/blog-changelog-index.kmd` (#049.59). Adds editorial post kind (long-form case studies, deep dives, design philosophy) alongside auto-generated changelog entries. Owner-curated content per feedback memory `kds_owner_curated_content`.

Spec — Blog editorial posts

Strict extension to blog-changelog-index.kmd (#049.59 ratified). This spec adds editorial post pipeline without breaking changelog behavior.

Princípios

  1. *wo kinds, one index*— changelog (auto-gen) + post (editorial); merge sorted by date desc.
  2. *wner-curated editorial*— IA não escreve posts autonomamente (per feedback_kds_owner_curated_content).
  3. *arkdown source*— editorial posts live in meta/docs/stack/blog/<slug>.md.
  4. *rontmatter contract*— required fields enforced.
  5. *ilterable*— index supports kind filter chips.

R-edit.1 — Source directory

Editorial markdown source:

meta/docs/stack/blog/
├── README.md
├── 2026-05-14-m3-expressive-launch.md
├── 2026-04-29-hub-package-pages-bug.md
└── ...

File naming: YYYY-MM-DD-<slug>.md (date prefix sorts naturally).

Changelog source remains in releases.toml per blog-changelog-index.kmd R1.

R-edit.2 — Frontmatter contract

Every editorial post has YAML frontmatter:

---
title: "Material 3 Expressive: what we shipped"
author: Rodrigo Mendonça
date: 2026-05-14
tags: [design, m3, expressive]
category: design-system
cover: og-images/m3-expressive-launch.png
excerpt: "Short summary for index card preview"
---
Field Required Notes
title yes Display name
author yes Display name (Koder user)
date yes ISO 8601
tags no Array of strings
category yes One of: design-system / engineering / product / process
cover no OG image path (relative to meta/docs/stack/blog/)
excerpt no Manual excerpt; auto-generated from first paragraph if absent

R-edit.3 — Index merge behavior

design-gen produces /blog/ index merging both kinds:

<ol class="blog-index">
  <li data-kind="post"     data-date="2026-05-14">Material 3 Expressive...</li>
  <li data-kind="changelog" data-date="2026-05-13">v0.22.0 koder_kit...</li>
  <li data-kind="post"     data-date="2026-04-29">Hub package pages bug...</li>
  ...
</ol>

Sort: data-date desc.

R-edit.4 — Filter chips

Index page renders filter chips at top:

  • *ll*(default; both kinds)
  • *hangelog*(only kind=changelog)
  • *ditorial*(only kind=post)
  • (Optional) Category chips per category field.
  • (Optional) Tag chips per most-used tags.

Filters update visibility client-side (no page reload); URL hash preserves filter state (#filter=editorial).

R-edit.5 — RSS feed

/blog/feed.xml includes BOTH kinds. RSS items have <category> matching frontmatter; client RSS readers can filter.

Redit.6 — Ownercurated editorial

Per feedback_kds_owner_curated_content:

  • IA NÃO escreve posts editoriais autonomamente.
  • IA pode escrever DRAFTS apenas se owner solicita explicitamente.
  • Owner sempre revisa antes de publicar.
  • Changelog entries permanecem autogen (de releases.toml); editorial requer commit signedoff-by owner.

Audit log: editorial commits MUST carry Reviewed-by: <owner> trailer.

R-edit.7 — Rendering

design-gen template extension:

  • New kind post registered.
  • Markdown parsed via existing markdown pipeline (mesmo do migrate kind).
  • Cover image rendered as hero at top of post body.
  • Excerpt rendered no index card.

T-suite

  • *1*Editorial post with full frontmatter: parses correctly; appears em index.
  • *2*Editorial post without optional fields (cover/excerpt): excerpt auto-extracts first paragraph; cover defaults to category icon.
  • *3*Filter chip "Editorial": only posts visible.
  • *4*Filter chip "Changelog": only changelog entries visible.
  • *5*RSS feed includes both kinds with correct categories.
  • *6*Circular include in {include} (markdown): detected + rejected.
  • *1*Editorial commit without Reviewed-by trailer: CI warns (não bloqueia, mas registra).

Source: ../home/koder/dev/koder/meta/docs/stack/specs/tools/blog-editorial-posts.kmd