惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
aimingoo的专栏
aimingoo的专栏
I
InfoQ
B
Blog RSS Feed
D
DataBreaches.Net
S
SegmentFault 最新的问题
P
Proofpoint News Feed
A
About on SuperTechFans
WordPress大学
WordPress大学
Hugging Face - Blog
Hugging Face - Blog
博客园 - 司徒正美
小众软件
小众软件
博客园 - Franky
有赞技术团队
有赞技术团队
D
Docker
T
Tailwind CSS Blog
雷峰网
雷峰网
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Blog — PlanetScale
Blog — PlanetScale
酷 壳 – CoolShell
酷 壳 – CoolShell
B
Blog
V
Visual Studio Blog
宝玉的分享
宝玉的分享
爱范儿
爱范儿

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
YAML Frontmatter for Content Pipelines: 50+ Articles With...
孫昊 · 2026-05-07 · via DEV Community

孫昊

TL;DR: Stop maintaining INDEX.md by hand. Add YAML frontmatter to every paste-ready file, then a 30-line scanner generates the index automatically. I have 50+ articles, 14 newsletters, and 6 SKU files all auto-indexed via this pattern.


The problem with INDEX.md

You start writing article 1. You add it to INDEX.md. Article 2 — you add it. Article 3, 4, 5...

By article 30, INDEX.md is out of sync. You forgot to add 3 articles. You added one with the wrong status. The status: ready flag is wrong because you published it but didn't update INDEX.md.

This is the classic "human-maintained source of truth" anti-pattern. Don't do this.

The frontmatter pattern

Every paste-ready file gets a YAML block at the top:

---
id: devto-55-manifest-frontmatter-content
title: "dev.to #55 - YAML Frontmatter for Content Pipelines"
category: content
priority: P1
status: ready
eta_min: 5
actions: [preview, copy-clipboard, open-devto]
tags: [content, yaml, automation]
created: 2026-05-07
publish_target_date: 2026-05-12
---

# YAML Frontmatter for Content Pipelines

(article body)

Enter fullscreen mode Exit fullscreen mode

The frontmatter has 9 fields:

  1. id — unique kebab-case ID
  2. title — display title
  3. category — content / product / roadmap / kit
  4. priority — P0 (today) / P1 (this week) / P2 (next month)
  5. status — draft / ready / live / published / archived
  6. eta_min — how long to publish/use it
  7. actions — array of dashboard buttons to render
  8. tags — for searchability
  9. created / publish_target_date — calendar tracking

The 30-line scanner

import yaml
from pathlib import Path

def scan_assets(root: Path):
    assets = []
    for md_file in root.rglob("*.md"):
        content = md_file.read_text(encoding='utf-8-sig')
        if not content.startswith("---"):
            continue
        try:
            _, yaml_block, _ = content.split("---", 2)
        except ValueError:
            continue
        try:
            meta = yaml.safe_load(yaml_block)
        except yaml.YAMLError:
            continue
        if not isinstance(meta, dict) or "id" not in meta:
            continue
        meta['_path'] = str(md_file.relative_to(root))
        assets.append(meta)
    return sorted(assets, key=lambda a: a.get('publish_target_date', '0'))

# Usage
assets = scan_assets(Path("reports"))
for a in assets:
    print(f"[{a['category']:8}] {a['priority']} {a['id']:40}{a['_path']}")

Enter fullscreen mode Exit fullscreen mode

Output:

[content ] P0 devto-50-50-articles-checkpoint            → devto-article-50-paste-ready.md
[content ] P0 substack-23-day-60-milestone               → substack-issue-23-day-60-milestone-paste-ready.md
[content ] P1 devto-55-manifest-frontmatter-content      → devto-article-55-paste-ready.md
...

Enter fullscreen mode Exit fullscreen mode

No manual updates. Adding a new file with frontmatter automatically appears in the index.

Dashboard rendering

The dashboard reads the frontmatter and renders it as a table:

from flask import render_template_string
@app.route('/assets')
def assets():
    rows = scan_assets(Path("reports"))
    return render_template_string(TABLE_HTML, rows=rows)

TABLE_HTML = """
<table>
{% for r in rows %}
  <tr class="{{r.priority}}">
    <td>{{r.category}}</td>
    <td>{{r.id}}</td>
    <td>{{r.status}}</td>
    <td>{{r.publish_target_date}}</td>
    <td>
      {% if 'preview' in r.actions %}<a href="/preview/{{r._path}}">preview</a>{% endif %}
      {% if 'copy-clipboard' in r.actions %}<button onclick="copy('{{r._path}}')">copy</button>{% endif %}
      {% if 'open-devto' in r.actions %}<a href="https://dev.to/new" target="_blank">dev.to</a>{% endif %}
    </td>
  </tr>
{% endfor %}
</table>
"""

Enter fullscreen mode Exit fullscreen mode

Three columns: ID, status, publish target date. Three buttons: preview, copy clipboard, open editor. Auto-generated from frontmatter.

Status transitions

The status field has 5 values that transition:

draft → ready → live (Substack/Gumroad) | published (dev.to) → archived

Enter fullscreen mode Exit fullscreen mode

Update via shell one-liner:

sed -i 's/^status: ready$/status: published/' reports/devto-article-50-paste-ready.md

Enter fullscreen mode Exit fullscreen mode

Or your dashboard exposes a button per row that does it.

What this saves

Manual INDEX.md update flow:

  • Write article → save file → open INDEX.md → add row → fix sort → save → commit

Frontmatter flow:

  • Write article (with frontmatter at top) → save file → done

Per-article time saved: 60 seconds.
For 50 articles: 50 minutes saved + zero index drift.

Bonus: stale-file detection

Once frontmatter is in place, you can write a stale-check:

from datetime import datetime, timedelta

stale = []
for a in scan_assets(Path("reports")):
    target = datetime.strptime(a.get('publish_target_date', '2099-01-01'), '%Y-%m-%d')
    if a['status'] in ['draft', 'ready'] and target < datetime.now() - timedelta(days=7):
        stale.append(a)

if stale:
    print(f"{len(stale)} stale assets:")
    for s in stale:
        print(f"  {s['id']} (target {s['publish_target_date']})")

Enter fullscreen mode Exit fullscreen mode

Run this in cron daily. Flags any draft you forgot to publish.

Source

Full manifest scanner + dashboard + stale checker:

AutoApp Dashboard ($39) includes:

  • manifest_scan.py (this article)
  • dashboard/app.py (Flask + frontmatter rendering)
  • stale_checker.py (cron-friendly)
  • 60+ paste-ready files with frontmatter as examples

If you have 10+ markdown files and a hand-maintained INDEX.md, you have a frontmatter-shaped problem. Solve it once.