6.1 KiB
wp-materialize – Agents Specification
Purpose
wp-materialize is an automation compiler that materializes specified Markdown files in Git repositories (or local directories) into WordPress posts. Git / filesystem state is the single source of truth; WordPress is a derived, materialized view.
The system is declarative, atomic, incremental, and fail-fast. It never guesses intent, never partially updates WordPress, and never mutates state unless correctness is proven ahead of time.
This document is the authoritative agent-facing spec to be fed to Codex or other build agents.
Core Principles (Non-Negotiable)
-
Git / Filesystem as Source of Truth WordPress content must exactly reflect declared Markdown sources and manifests.
-
Declarative Configuration Only No implicit discovery, no heuristics, no inference.
-
Atomic Execution A full dry-run validation must succeed before any WordPress mutation occurs.
-
Incremental Updates Only content whose source timestamp is newer than its cached materialization timestamp may be updated.
-
Fail Fast, Fail Loud Any configuration, validation, or conversion error aborts the entire run.
High-Level Architecture
The system operates in two strictly separated phases:
Phase 1: Pure Evaluation (Dry Run)
- Read global config
- Discover and load repositories / directories
- Load and validate all
.wp-materialize.jsonmanifests - Resolve inheritance (categories, tags, subdirectories)
- Convert Markdown → HTML (in-memory only)
- Resolve titles, timestamps, taxonomy
- Determine incremental update set
- Detect all errors
If any error occurs, execution stops here.
Phase 2: Side-Effect Application
- Create missing WordPress categories
- Create or update WordPress posts
- Update cached timestamps only for successfully applied posts
Global Configuration
Location:
.config/wp-materialize/config.json
Responsibilities:
- WordPress root directory (where
wpCLI is executed) - Repository storage directory
- List of Git repositories to clone / pull
- List of non-git directories to manage
Runtime State (Separate from Config)
Mutable runtime state must be stored separately, e.g.:
.config/wp-materialize/state.json
State includes:
- Last successful materialization timestamp per post
- Cached per-post source timestamps
Config must remain declarative and diffable.
Repository and Directory Rules
- Each managed directory must contain a
.wp-materialize.jsonmanifest. - Any directory listed under
subdirectoriesmust contain its own manifest. - Missing manifests are hard errors.
- No implicit recursion is allowed.
Per-Directory Manifest: .wp-materialize.json
Each manifest defines a scope boundary.
Top-Level Fields
categories
{
"content": ["Systems", "Infrastructure"],
"inherit": true
}
content: array of category pathsinherit: true→ append to parent effective categoriesinherit: false→ override parent categories entirely
tags
{
"content": ["automation", "wordpress"],
"inherit": true
}
Semantics identical to categories.
subdirectories
{
"content": ["design", "notes"],
"inherit": true
}
- Controls traversal explicitly
- Included subdirectories must have their own manifest
inherit: falsecuts traversal
File-Level Configuration
Each file listed under files represents a WordPress post.
"files": {
"post.md": {
"title": "Explicit Title",
"categories": { "content": ["Overrides"], "inherit": false },
"tags": { "content": ["extra"], "inherit": true }
},
"essay.md": {
"use_heading_as_title": {
"level": 1,
"strict": true
}
}
}
Title Rules
-
If
use_heading_as_titleis specified:- Extract the specified heading level
- Use it as the WordPress post title
- Remove that heading from the body
- Promote remaining headings by one level
- If
strict: true, exactly one matching heading must exist
-
Otherwise,
titlemust be provided
Markdown → HTML Conversion
- Conversion occurs only during dry run
- No HTML is written or sent to WordPress during evaluation
- Conversion errors are fatal
Category Materialization
-
Categories are treated as hierarchical paths
-
If a declared category path does not exist in WordPress:
- It is automatically created during the apply phase
-
Category creation:
- Must be planned during dry run
- Must occur before post updates
Tags are not auto-created.
Timestamps and Incremental Updates
Timestamp Sources
-
Git repository:
- Use Git commit timestamps
-
Non-git directory:
- Use filesystem timestamps
The source of timestamps must be deterministic per repository.
Cached Metadata
- Each post stores a cached source timestamp representing the last successful materialization
- Failed runs must not update cached timestamps
Incremental Rule
On each run:
- Compare current source timestamp vs cached timestamp
- Only posts where
source_timestamp > cached_timestampare eligible for update - Unchanged posts are treated as no-ops
Post Identity
Each WordPress post must store stable metadata:
_wp_materialize_source = <repo_name>:<relative_path>
This identity is used for:
- Idempotent updates
- Safe renames
- Incremental comparison
Atomicity Guarantee
-
If any dry-run validation fails:
- No WordPress calls are executed
- No categories are created
- No cached timestamps are updated
-
Apply phase executes only after full validation succeeds
Error Handling
All errors are fatal:
- Missing manifests
- Invalid inheritance
- Invalid Markdown
- Missing or ambiguous titles
- Invalid category/tag resolution
- Timestamp resolution failures
No warnings. No partial success.
Implementation Notes
-
Language: Python
-
The implementation must prioritize:
- Determinism
- Readable error messages
- Testable pure functions for evaluation phase
This document is the contract. Implementation must not relax or reinterpret it.