Relationships
Implemented by 5
Branches 2
Expose file-level
createdandmodifiedtimestamps as Markdoc variables so any rune can consume them as attribute defaults, with explicit attribute values taking precedence.
Problem
Plan runes (and potentially other runes) benefit from knowing when content was created and last modified. The plan-activity rune already derives mtime from git via a standalone scanner (runes/plan/src/scanner.ts), but this is package-specific, runs outside the content pipeline, and only provides modification time — not creation time.
There is no general-purpose mechanism for runes to access file timestamps. Authors who want timestamps must manually maintain them in frontmatter, which is tedious and error-prone.
Design
Markdoc Variables
Inject two new variables into every page's Markdoc transform config:
| Variable | Type | Description |
|---|---|---|
$file.created | string (ISO 8601 date) | When the file was first committed |
$file.modified | string (ISO 8601 date) | When the file was last committed |
These join the existing $page and $frontmatter variables already injected in packages/content/src/site.ts.
Timestamp Resolution Order
A three-tier fallback chain, consistent with the approach already proven in the plan scanner:
- Frontmatter override (highest priority) — if
createdormodifiedappear in frontmatter, use those values directly. This gives authors full control. - Git history — derive timestamps from
git log. This is the expected default for any content in a repository. - Filesystem stat (lowest priority) — fall back to
fs.stat().birthtimeMs/fs.stat().mtimeMs. Only meaningful for local-only content that has never been committed.
When none of the above produce a value, the variable is undefined and runes that consume it should handle the absence gracefully (e.g., omit a date display).
Rune Consumption
Runes opt in by declaring created and/or modified attributes with variable defaults:
{% spec id="SPEC-001" created=$file.created modified=$file.modified %}