V0.41.0
Name:v0.41.0Status:complete

v0.41.0 — Composed runes you can ship

v0.40.0 built composition and proved it on bond and character. Nobody outside the repo can use it yet: a definition reaches the pipeline only as a string passed to defineComposedRune. This milestone closes that gap. A plugin ships definitions from a declared directory, a project writes its own in runes.dir, and the authoring guide makes it a documented feature (SPEC-153, SPEC-145).

It also takes the storytelling replacement (SPEC-147) from two runes to five, leaving only the two that need new primitives.

Milestone burndown: 0 open work items remaining; peak 7, started Oct 80246Oct 8Oct 11
Open work items Ideal burndown
Progress 8/8 work items
Related 16
History 4
  1. 5ff38d6
    statusplanning→complete
    by bjornolofandersson
  2. 33aa1ad
    Content editedby Claude
    Close v0.41.0: milestone complete, SPEC-153 implemented
  3. c2186ab
    Created (planning)by bjornolofandersson
  4. fad3ced
    Content editedby Claude
    plan: draft v0.41.0 — Composed runes you can ship

Work Items

Done 8
WORK-631 main
Content no content-model field matches is reported, not silently dropped
Found by WORK-625, the composed character. A {% hint %} an author wrote before the first section was matched by no preamble field and dropped, with no error and no warning. The composed definition works around it with a catch-all body preamble field. SPEC-145 records the finding beside the character worked example.D11 promises against this silent loss but checks the other direction. It requires that every declared field is placed by a slot. Nothing checks that every authored node is matched by a field. So content can still vanish between the author's file and the content model, by another route.This applies to any content model, not just compositions. A tree-owning rune with a narrow preamble loses unmatched content the same way, so the fix belongs in the content-model resolver, not in the composition layer. First measure how many shipped runes drop content today, so the change can be staged as a warning before an error if needed.
high moderate
5/5 criteria
WORK-632 main
A by-attribute schema table's rows must cover the attribute's matches
SPEC-145 D27 (c). validateSchemaTable (packages/runes/src/lib/schema-table.ts) checks two things: that by names a declared attribute, and that a fallback exists. It never compares rows keys with that attribute's matches. So a misspelt row (podcasts:) or a newly added enum value silently selects the fallback, and {% playlist type="podcast" %} would publish MusicAlbum.D15 already requires an exact two-way correspondence for template variants. The schema by gets the same check. It needs no composition, and it applies to every hand-written table today (playlist, track, organization and others).
medium simple
5/5 criteria
WORK-633 main
A plugin ships composed runes from a declared rune directory
SPEC-153 implementation note 3, the plugin side. In v0.40.0 a composed definition reached the pipeline only as a string passed to defineComposedRune. A plugin now declares runeDir (package-relative, resolved the way fileRoots resolves, D2). Every <rune>.md in it is loaded as a composed rune.Decisions:
high complex
8/8 criteria
WORK-634 main
A project defines its own composed runes in runes.dir
SPEC-153 implementation note 4, the project side, and the step that opens composition to users. A project declares runes.dir in refrakt.config.json, following contentDir's shape, with a recommended default of runes (D4). The loader discovers every <rune>.md in it.Decisions:
high complex
7/7 criteria
WORK-635 main
lore as a composed rune
SPEC-147 lists lore as the simplest storytelling rune. It has a title, a body, an Article schema row (title → headline, category → articleSection) and a registered entity. It follows the pattern WORK-624 and WORK-625 set:
medium simple
5/5 criteria
WORK-636 main
realm and faction as composed runes
SPEC-147: realm (Place) and faction (Organization) share character's shape, sections plus a preamble, and each has a -section child rune that composition removes. Their media split maps to {% mediatext %}. Both follow WORK-625's pattern:
medium moderate
7/7 criteria
WORK-637 main
The authoring guide for composed runes
Composition ships to users in this milestone, and SPEC-153 D9 makes the definition file the user-facing authoring surface. Today the only documentation is a PluginRune note in rune-authoring/authoring-overview.md. Add a page under site/content/extend/rune-authoring/ written from SPEC-145's "template vocabulary, in one place" table. The guide is the spine of that table rather than a summary of it.It covers:
high moderate
6/6 criteria
WORK-638 main
Plan runes keep the content their content models drop, then content-unmatched becomes an error
WORK-631 added content-unmatched: validation reports an authored node that no content-model field matches, because the transform drops it. It shipped as a warning because the measurement found that four shipped runes drop real content today, all in the plan plugin. The plan site renders each entity through its rune ({% expand %}), so the loss reaches plan.refrakt.md.Measured over this repo's plan/ directory (907 files), as of WORK-631:
high moderate
5/5 criteria