The identity guard is path-granular Implement SPEC-158. IDENTITY_FIELDS (packages/transform/src/identity-fields.ts) becomes a list of paths, and findReservedFields resolves a wildcard segment. This adds three protections:
high simple
A rune config may omit block SPEC-145 D2a. A composed rune has no BEM block and ships no CSS, but it still needs an engine config: its modifiers, universal attributes and meta blocks have to render. Today RuneConfig.block is required. The engine builds ${prefix}-${config.block} unconditionally (packages/transform/src/engine.ts), so a config without one emits rf-undefined. A rune with no config at all leaks data-rune-fields into the HTML.Make block optional:
high moderate
data-owner and data-slot survive a primitive's transform SPEC-145 D10a, landed ahead of composition so it can be proved inert.Markdoc keeps only the attributes a node's schema declares. A marker set on a node before a primitive transforms it is therefore dropped, unless every node and tag schema declares it. Declare data-owner and data-slot once, at config assembly, on every node and tag schema, never per rune. Teach the schema-table resolvers (packages/runes/src/lib/schema-table.ts) to admit a node past a boundary by data-owner plus data-slot. A primitive's own data-name on the same node is left alone.In the output, releaseOwnedNodes strips data-owner and keeps data-slot (D10a step 4, D10c).Nothing sets either attribute yet, so the gate is that nothing moves. The survival test runs the markers through every rune in D12's placement set. That set is "no peer schema, no requiresParent". Any rune that drops either attribute is a finding, named by the test.
high moderate
A rune can be defined by a composition template The mechanism at the centre of SPEC-145. A definition is frontmatter (the input declaration: tag, attributes, content model, schema, registers) plus a Markdoc body (the output template). Slot names are the join between them.This item builds the definition → PluginRune path and the template's render. It covers only the vocabulary the two slices need:
high complex
A composition definition is checked at construction Every rejection SPEC-145 decides at definition load or schema construction, in one place, so that a bad definition fails with its rune and the offending name rather than rendering silently wrong. Between them, D11 and D26 match every content-model field to exactly one slot.The bulk of this item is the error messages. Each check names the rune and the thing it rejected, and the tests assert the message as well as the throw.
high moderate
Slice 2 — character as a composed rune, against the SEO baseline SPEC-145's full worked example and its spike criterion. It is the first composed rune with a schema row: Person, which a first-party definition may declare under D25. It is also the first that uses sections with each and $each.heading, and a preamble with an image slot.character is the case WORK-617 was cancelled for. Its transform failed SPEC-143's D4 family test, so it is replaced rather than declared. The composed version drops the character-section child rune entirely.Like WORK-624, it ships beside the plugin (SPEC-147 D1). The reference is the storytelling character fixture in contracts/seo-baseline/. The gate is that every difference from it is explained, not that there are none (SPEC-147 D2). Two differences are already decided:
high complex
An npm pack → install → load harness for plugins SPEC-153 implementation note 2. It is the only gate that catches the measured breakages, and nothing like it exists today. A monorepo test resolves packages through workspace links, so it passes whatever the tarball contains.The harness packs a plugin, installs the tarball into a temporary fixture project outside the workspace, and loads it through the same loadPlugin() path a site uses. The first target is the plugins as they ship today. That proves the harness on known-good input, and proves WORK-626's fix against the tarball rather than the workspace. Shipping composed runes in a tarball is a later SPEC-153 step that reuses this harness.
medium moderate
A composition template places a declared meta block with {% metablock %} SPEC-145 D7. A composed rune has no layout, and the engine renders a declared blocks entry only through layout. So a composed rune can declare metaFields and blocks in full and get nothing, with no diagnostic.WORK-622 landed the generated config carrying both but left its "meta blocks render" criterion unchecked for this reason (#683). character's blocks.metadata (role and status as a definition list) needs it for WORK-625. Pulled into v0.40.0 on 2026-10-08 rather than dropping the block from the composed character. Placing a badge or deflist rune instead is the channel SPEC-145's authoring note rules out for attribute values.
high moderate