Content Model
Documentation Posts
References are standard WordPress post entries distinguished by a meta key:
_id86_content_type = <frontmatter content_type> (from the Markdown source)
The child theme filters homepage cards and the Fuse.js search index against this marker. A post is part of the documentation experience when _id86_content_type holds one of the registered content types.
Block content: the body is stored in post_content as serialized Gutenberg block markup (generated by the sync pipeline's blockify), so every documentation post opens directly in the block editor. See 4. WP Ops → Gutenberg Block Reference for the block inventory and serialization formats.
Content type values
_id86_content_type drives the content-type badge on archive cards (id86_archive_card_shortcode). The single source of truth is the id86_content_types() registry in the child theme (also exposed as the id86_content_types filter), which mirrors the approved Content Types plus an other catch-all:
| Value | Badge shown |
|---|---|
cheatsheet | Cheatsheet |
reference | Reference |
checklist | Checklist |
glossary | Glossary |
prompt | Prompt |
snippet | Snippet |
pattern | Pattern |
template | Template |
runbook | Runbook |
other | Other |
| (unset / unknown) | Other (fallback) |
Manual override (Content Type metabox)
Each post editor shows a Content Type sidebar dropdown (the ten registered values, or blank to clear) registered by the Fluent Snippets PHP snippet 3-content-type-metabox.php (run_at=backend, storage wp-content/fluent-snippet-storage/). The dropdown options are loaded from the theme's id86_content_types filter, so registering a new type in the theme automatically adds it to the editor. Saving a post writes the selected value to _id86_content_type.
Automation behavior
Every Markdown source must declare content_type in its frontmatter (a registered type from Content Types). The sync bridge (tools/wp-sync-bridge.php) writes that value to _id86_content_type only when the meta is empty (new posts). A manually chosen value is preserved across future syncs — automation never overwrites a manual override. Set or inspect it manually with:
wp post meta update <id> _id86_content_type cheatsheet # -> "Cheatsheet" badge
wp post meta update <id> _id86_content_type reference # -> "Reference" badge
wp post meta get <id> _id86_content_type
The legacy value instant-documentation (pre-registry) was migrated to reference.
Source Metadata
Each documentation post carries two additional meta keys for sync tracking:
| Key | Purpose | Example |
|---|---|---|
_id86_source_path | Repository-relative Markdown source path | code/git/git-cheat-sheet.md |
_id86_source_hash | SHA-256 content hash for change detection | b5a15644535cd33c42322bed4c8361285c21daf621fddcf297739b0067944aa1 |
Categories
Categories are locked to a single-select of 4 main categories. The four main categories drive the site chrome and are published and shown on the homepage:
| Slug | Display Name | Tier | Current Post Count |
|---|---|---|---|
code | Code | Main | 2 |
devops | DevOps | Main | 2 |
ai | AI | Main | 1 |
security | Security | Main | 1 |
Auxiliary categories also exist:
| Slug | Notes |
|---|---|
demo | Hidden system category hosting the article-body styling verification fixture (styling-sample) |
testing | Left over from frontmatter verification; no posts |
Auxiliary categories (demo, testing) never appear on the homepage.
Categories determine:
- The hero quick-topic links.
- The niche card section (4 cards, one per category).
- The color accent on each card (via
.id86-code,.id86-devops,.id86-ai,.id86-securityCSS classes). - The category archive pages.
New top-level folders auto-create their WordPress category on sync (e.g. demo/ → demo), so auxiliary categories do not require manual setup.
Note on
seovssecurity: the content model listssecurityas a main lane, but the register and live site still hold aseocategory (SEO-000001). Migrating the liveseocategory/post tosecurity(or vice versa) is a separate follow-up; the register documents the current state.
Post Views
Each single post view increments post_views_count post meta. This is used by the optional popular-post query via the order_by_views GenerateBlocks class.
Markdown Source Repository
The pre-publication Markdown source of truth lives at:
/home/rezriz/github/ID86-instant-documentation
Repository: github.com/donnyaw/ID86-instant-documentation
Content is authored in Markdown and synchronized to WordPress for initial publication. After publication, WordPress Gutenberg becomes authoritative for live content. Later Markdown syncs protect the published post and do not overwrite its body or editorial metadata.
Authoring happens in the local register mirror (/home/rezriz/github/seo-content/web-project/id86.net/content/). New documents are created with scripts/new-doc.py, which generates the file named PID-title.md (e.g. content/devops/DEV-000003-tmux-split-panes-cheat-sheet.md) plus the matching content-inventory.csv row in one command. Each file carries an explicit slug: frontmatter field so WordPress URLs stay clean (/devops/tmux-split-panes-cheat-sheet/) even though the filename embeds the PID. See Content Register — New Document Workflow.
Editing Content
Before publication, Markdown is the source of truth for content. After publication, WordPress Gutenberg is authoritative for live content; the CSV register remains the source of truth for lifecycle and identity. The register (content-inventory.csv) documents every column, the status lifecycle, content types, PID rules, and validation tooling — see Content Register. To update a post:
- For an unpublished post, edit Markdown in
content/and validate it. - Publish the post once through the scheduled publishing or sync workflow.
- For a published post, edit the post directly in WordPress Gutenberg. Do not edit its Markdown expecting the live post to change.
- Use the CSV for lifecycle, PID, keyword, and audit identity updates.
| Field | When to edit |
|---|---|
| Body content | Almost always — the actual update |
title | When renaming the post |
description | When the summary / meta description changes |
tags | When adding or removing tags — multi-select, comma-separated, maximum 5 tags |
date | Only to change the publish date |
id / category / slug / status | Never — structural (id/category), URL-breaking (slug), or CSV-controlled (status) |
Two different dates:
| Field | Format | Meaning | Who updates it |
|---|---|---|---|
Frontmatter date | YYYY-MM-DD (date only) | WordPress publish date shown in the post | You, only when re-dating |
CSV Updated column | YYYY-MM-DD | When the file was last edited | Auto — refreshed by the validate script from the file timestamp |
Date convention: the frontmatter
datefield uses date only (YYYY-MM-DD) — no time-of-day component. WordPress stores the post at00:00:00on that date. A future date makes WordPress hold the post asfuturestatus and auto-publish it when the WordPress scheduler runs on that day, sodatedoubles as the scheduling mechanism on the sync path.
The Updated refresh is automated by an hourly systemd user timer (id86-inventory-refresh), which runs validate-inventory.py --fix automatically from the file save-times.
Two CSV-driven scripts in wp-ops (01-operations/content-management/) control the flow:
| Script | CSV Status | Effect |
|---|---|---|
id86-publish-scheduled.sh | scheduled | Sends the markdown to WordPress directly (via WP-CLI bridge) when Publish Date <= today, then flips the row to published |
id86-push-published.sh | published | Copies the markdown into the canonical GitHub repo and pushes to main, where GitHub Actions syncs it to WordPress (SSH fallback: tools/id86-sync.sh locally) |
The register's Publish Date column (format YYYY-MM-DD) holds the target go-live date and is required for scheduled rows. When that date arrives, the publish script sends the post to WordPress and automatically flips the status to published.
Content Templates
Single posts use two GeneratePress Elements:
- #Single Post Title (ID 3663, Page Hero): Breadcrumbs, H1 title, author, date.
- #Single Post Content (ID 1371, Content Template): Featured image, post content, tags, related posts, comments.