V0.38.0
Name:v0.38.0Status:complete

v0.38.0 — Rune transform consolidation

Across 121 runes, transform() is 3,955 lines. A measurable share of that is not logic — it is dead code, guards that guard nothing, and the same four gestures retyped. Separately, the declarative content model already states more about a rune's authoring syntax than any tool surfaces, and one field of it silently eats authored content.

This milestone removes what is provably inert, names the idioms that are merely repetitive, and closes the two gaps where a declaration exists and does not reach either the author or the runtime.

Milestone burndown: 1 open work item remaining; peak 1, started Sep 2801Sep 28Oct 11
Open work items Ideal burndown
Progress 8/10 work items
Related 15

Work Items

Draft 1
WORK-605 main
playlist adopts field-level emitTag, keeping a minimal inheritance pass
With WORK-604 landed, playlist's tracks field can declare emitTag: 'track' and receive one homogeneous, document-ordered array instead of a mix of list nodes and track tags.That deletes the interleaving machinery at plugins/media/src/tags/playlist.ts:316-353 — the listCursor consuming tracksData by list length, and the defensive tail for "a shape the resolver collected but this loop did not recognise".It does not delete adoptNestedTrack. See below.
medium moderate
0/8 criteria
Ready 1
WORK-607 main
plan migrate ids treats an ID present on the base ref as fixed
runMigrateIds sorts colliding claimants by path and keeps the first (plugins/plan/src/commands/migrate-ids.ts:165), so on a branch it can renumber the entity already on main and leave the local draft holding the ID.The safety argument in the code — that it only acts when nothing references the ID — rests on a check that reads plan files only (migrate-ids.ts:170-182). Plan IDs are referenced from four published CHANGELOGs (runes 130, transform 120, lumina 80, content 51), where a renumber is permanent and invisible.
medium simple
0/6 criteria
Done 8
WORK-598 main
Drop inert metas from rune children arrays
100 metas across 24 tag files are mapped in properties and emitted into children, where createComponentRenderable filters them straight back out (packages/runes/src/lib/component.ts:127-131). 133 other metas already omit that emission and render identically.Delete the emission. Nothing else.
high simple
5/5 criteria
WORK-599 main
Remove the unreachable schema.org channel from createComponentRenderable
schema, typeof and schemaOrgType on TransformResult / InlineTransformResult have zero callers across all 121 runes. SPEC-130 moved every rune to the declarative table on createContentModelSchema({ schema }); the imperative surface it replaced was never deleted.So schemaTags (component.ts:57-61) is always empty, isSeoMeta (:84) is always false, emptySeoMetas (:110-125) is always empty, and roughly 40 lines of the helper are unreachable.
high simple
6/6 criteria
WORK-600 main
Drop redundant undefined guards in refs and properties literals
createComponentRenderable opens both slot loops with if (v === undefined) continue (component.ts:77 and :98), so inside refs and properties:
medium trivial
4/4 criteria
WORK-601 main
Consolidate extractText and adopt the exported textContent
Two families of duplication, with different fixes.AST side — six byte-identical copies. extractText(node: Node) is defined identically in packages/runes/src/tags/form.ts, plugins/places/src/tags/map.ts, plugins/design/src/tags/{palette,typography,spacing}.ts and plugins/marketing/src/tags/comparison.ts.Renderable side — textContent is already exported and reimplemented anyway. @refrakt-md/runes exports it (seo.ts:23), and plugins/plan/src/pipeline.ts + plugins/storytelling/src/pipeline.ts define extractTextContent, while plugins/media/src/tags/track.ts defines a Tag-flavoured extractText.
medium simple
4/4 criteria
WORK-602 main
Add renderNodes and the bodyOnly content-model preset
Two spellings, each repeated past the point of being idiom.renderNodes(nodes, config) → RenderableNodeCursor. The full form appears 153 times, 81 of them as exactly new RenderableNodeCursor(Markdoc.transform(asNodes(resolved.X), config) as RenderableTreeNode[]). There are 160 as RenderableTreeNode[] casts in the tag files.bodyOnly() — { type: 'sequence', fields: [{ name: 'body', match: 'any', optional: true, greedy: true }] } is the entire content model for 49 of the 94 declared in the tag files, in four whitespace variants.
medium simple
5/5 criteria
WORK-604 main
Pass authored tags through a mixed list|tag:x field with emitTag
resolveSequence's emit branch skips anything that is not a list, then replaces the field with only the emitted nodes (packages/runes/src/lib/resolver.ts:194):
high simple
5/5 criteria
WORK-606 main
Project itemModel and render emitTag and template in the rune reference
For a rune whose primary syntax is a Markdown list, refrakt reference documents that a list is accepted and nothing about what goes in it. Three facts are lost at two points:
medium moderate
6/7 criteria
WORK-608 main
Promote the three styles transform helpers into the named-transform enum
RuneConfig.styles[…].transform is one of only two function-typed fields in the whole of RuneConfig (packages/transform/src/types.ts:301; the other is postTransform, which stays imperative by design). Unlike postTransform, it does not need to be a function: a declarative sibling already ships, and the function form is used by a closed set of three helpers.
medium simple
8/8 criteria