- `album` no longer required; category format changed to Category:Album:<name> - New optional `categories` list for additional arbitrary categories - `wiki.siblings` bill of materials: path+tag required for resolution, label optional - Post-Pandoc [[TAG]] substitution with four-tier fallback (link+label, link, plain label, plain tag) - Sibling map built from all published posts in the same run; unresolved siblings degrade gracefully - SCHEMA.md fully updated; README.md deferred items moved to implemented Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
9.3 KiB
SCHEMA — song source files
Source-file contract for the songs pipeline. Files in mikkeli/ncmr-songs must follow this contract to be picked up by the auto-publisher.
The schema lives entirely in the YAML frontmatter block at the top of each .md file. The body below the frontmatter is plain markdown, rendered to MediaWiki wikitext by Pandoc — no special body conventions are enforced beyond the standard markdown grammar, except that [[TAG]] placeholders declared via wiki.siblings are substituted with resolved wikilinks after rendering.
File layout
---
title: pulse under latent semantic envelopes
album: Shirakaba Express Service
wiki:
publish: true
siblings:
- path: pulse_dev.md
tag: PULSE_DEV
label: development notes
release_date: 2026-06-18
lrc: pulse.lrc
---
# pulse under latent semantic envelopes
... body markdown, [[PULSE_DEV]] used inline ...
The renderer parses the frontmatter, validates required fields, then hands the body (with the first # Heading line removed) to Pandoc, then applies sibling substitutions on the wikitext output.
Fields
Required
title (string)
The MediaWiki page name that the bot writes or updates. MediaWiki capitalises the first letter automatically; lowercase input is fine.
Example: title: pulse under latent semantic envelopes → wiki page Pulse under latent semantic envelopes at URL /wiki/Pulse_under_latent_semantic_envelopes.
The body should contain a matching # Title first-line heading for human readability when viewing the source file directly (in an editor, on Gitea's blob view, etc.). The renderer strips this first-line heading during conversion — the wiki page already supplies its own title.
wiki.publish (boolean)
Publishing gate. true means the pipeline writes this file to the wiki. false means the file is ignored.
The wiki key is a mapping rather than a flat field so future toggles (wiki.protected, wiki.summary_template, etc.) can be added without restructuring.
Files under wip/ are also skipped, regardless of wiki.publish.
Optional
album (string)
The full album name. Drives [[Category:Album:<album name>]] injection at the bottom of the rendered wiki page.
Example: album: Shirakaba Express Service → category tag [[Category:Album:Shirakaba Express Service]].
Pages without an album (companion documents, placeholders, songs without an assigned album) simply omit this field — no category is injected unless categories provides one.
categories (list of strings)
Additional wiki categories to inject, beyond the album category. Each entry becomes [[Category:<value>]].
categories:
- Song development notes
- Work in progress
album and categories are additive — a page can have both.
release_date (ISO 8601 date)
The song's release date in YYYY-MM-DD format. Skipped if absent. Currently informational; may drive sort order on future auto-generated indexes.
lrc (string, relative path)
Pointer to a companion LRC file, relative to the .md file's location. The pipeline uploads the file to the wiki as <title>.lrc (if the content has changed) and appends a [[Media:<title>.lrc|Synced lyrics (.lrc)]] link to the page footer.
Example: in ses/pulse.md, lrc: pulse.lrc declares that ses/pulse.lrc is the companion synced-lyrics file.
If the LRC file is missing or not valid UTF-8, the upload is skipped with a warning — the page still publishes.
wiki.siblings (list of sibling declarations)
Bill of materials for inter-page wikilinks. Each entry declares a sibling .md file and a tag that can be used as a [[TAG]] placeholder anywhere in the body.
wiki:
siblings:
- path: pulse_dev.md # relative to this file's directory
tag: PULSE_DEV # placeholder used in body as [[PULSE_DEV]]
label: development notes # optional display text
Fields per entry:
| Field | Required for resolution | Purpose |
|---|---|---|
path |
yes | Relative path to the sibling .md file |
tag |
yes | Placeholder identifier; pipeline hunts for [[TAG]] in the rendered wikitext |
label |
no | Display text for the resolved link |
An entry missing either path or tag is silently skipped — no substitution is attempted and any [[TAG]] in the body passes through as a literal wikilink to a page named TAG.
Resolution:
The pipeline resolves each declared sibling's path to its wiki page title by looking it up in the set of all publishable files processed in the same run. Substitution rules:
| Condition | [[TAG]] becomes |
|---|---|
path resolves to a published page, label present |
[[wiki page title|label]] |
path resolves to a published page, no label |
[[wiki page title]] |
path not in published set (file exists but not published), label present |
label (plain text) |
path not in published set, no label |
TAG (plain text) |
Declared siblings whose file doesn't exist on disk are a validation error. Siblings that exist but aren't published (no wiki.publish: true) degrade gracefully per the table above.
The siblings list is a bill of materials — declaring a sibling has no effect unless its [[TAG]] appears in the body. Unused entries are inert.
--files mode: sibling resolution is limited to the files in that batch. Siblings pointing to files outside the batch degrade to plain text. This is acceptable since the pipeline always runs --all in the workflow.
Body
Plain markdown. The renderer applies two transformations before and after Pandoc:
- Pre-Pandoc: strip the first-line
# Headingif present. - Post-Pandoc: substitute
[[TAG]]placeholders declared inwiki.siblings.
Do not embed raw wikitext-specific syntax ({{Template}}, raw [[Wikilink]] not declared via siblings, etc.) in the body unless you intend the literal output. Use markdown idioms; the renderer adds the metadata-derived bits (banner, LRC link, categories, sibling resolution) around Pandoc's output.
Renderer behaviour
For each .md with wiki.publish: true and not under wip/:
- Parse and validate frontmatter.
- Strip the leading
# Headingfrom the body if present. - Pipe the body through
pandoc -f markdown -t mediawiki. - Substitute
[[TAG]]placeholders with resolved[[Page|Display]]wikilinks (or fallback text). - Prepend the auto-generated banner:
{{Auto-generated|source=<source URL>|commit=<sha>}}. - If
lrcis declared: upload the LRC file if its SHA1 has changed; append a[[Media:…]]link. - Append category tags:
[[Category:Album:<album>]]ifalbumis set;[[Category:<cat>]]for each entry incategories. - Read the current wiki page content via the MediaWiki API; if identical to the generated output, skip the write (idempotency — keeps the wiki history clean).
- If the page exists and does not start with
{{Auto-generated, skip with a warning (manual page protection). - Otherwise, write the page with an edit summary referencing the source commit.
Implemented scope
- Frontmatter parsing and validation
- Pandoc-based markdown → wikitext body rendering
- First-line h1 stripping
- Banner template injection
- LRC file upload and footer link
- Sibling placeholder resolution (
[[TAG]]→[[wiki page title|label]]) - Album category injection (
[[Category:Album:<name>]]) - Additional categories via
categorieslist - Idempotent writes (no-op skip when content matches)
- Manual-page protection (skip pages without auto-gen banner)
- Files without
wiki.publish: truesilently skipped
Not in scope (intentional)
- Multi-page aggregation: no album index pages generated; each file → its own wiki page. Album landing pages are wiki category pages, written freeform by the user.
- Language-aware rendering: the body conveys what languages exist; the renderer doesn't introspect.
Examples
A released song
---
title: pulse under latent semantic envelopes
album: Shirakaba Express Service
wiki:
publish: true
siblings:
- path: pulse_dev.md
tag: PULSE_DEV
label: development notes
release_date: 2026-06-18
lrc: pulse.lrc
---
A companion/sibling document (no album)
---
title: pulse under latent semantic envelopes - development notes
wiki:
publish: true
categories:
- Song development notes
---
A placeholder (not published)
---
title: (placeholder)
album: kairo
wiki:
publish: false
---
A WIP file (in wip/, skipped regardless)
---
title: 海淀
wiki:
publish: false
---
Validation errors the renderer must produce
Files without a wiki.publish: true frontmatter field are silently skipped — no error. The pipeline fails fast and loud only on files that are opted in (wiki.publish: true) with invalid data:
- Missing or empty
titleon a file withwiki.publish: true wiki.publishpresent but not a booleanrelease_date(if present) does not parse as YYYY-MM-DDcategories(if present) is not a listwiki.siblingsentry that has bothpathandtagdeclared, but thepathfile does not exist on diskwiki.siblingsentry that is not a mapping- Two publishable source files declare the same
title(page-name collision)
Validation runs before any wiki API calls. A failed validation aborts the run with a non-zero exit and leaves the wiki untouched.