Not all of knownSections is a gap
model being ignored here is principled and should stay that way: once a section is handed to a child rune, that rune's own schema owns its content model, and a parent-declared body model would be a second authority over the same content (ADR-028).
The other three fields are the gap. They are about heading matching, not body structure, and every one of them is meaningful under emitTag:
| Field | Purpose | Meaningful with emitTag? |
|---|
alias | ## AC → Acceptance Criteria | yes — emitAttributes: { name: '$heading' } should be able to emit the canonical name |
canonicalSlug | language-stable data-name | yes — same cross-locale CSS/anchor problem |
i18nAliases | ## Akzeptanzkriterien matches | yes — same |
model | body content model | no — the child rune owns it |
Steps to Reproduce
Declare a sections model with both emitTag and a knownSections entry carrying an alias, then author a section using the alias.
Expected
Either the alias resolves — $heading emits the canonical name — or the schema is rejected at construction time for declaring something that cannot take effect.
Actual
The alias is ignored. emitAttributes: { name: '$heading' } emits the raw heading text, so ## AC and ## Acceptance Criteria produce two different child runes with two different names and two different slugs.
Notes
No rune combines them today, so there is no live defect. The split is clean: work, bug and decision declare knownSections and no emitTag; accordion, character, realm, faction, reveal, timeline and itinerary declare emitTag and no knownSections. Severity is minor for that reason — this is a trap, not a failure.
It is a trap with no guardrail. The combination reads as obviously sensible, type-checks, and does nothing.
Two fixes, not equivalent:
- Make it work — run
matchKnownSection in the emitTag branch too, and let $heading resolve to the canonical name (with the raw text still available, since some runes will want what the author wrote). Adds a capability; changes no existing output, because nothing declares both. - Reject it — error at schema construction when
knownSections is declared alongside emitTag, naming model as the one field that would have been ignored anyway. Cheap, and makes the limit visible. Preference is (1): the aliases are the reason knownSections exists (WORK-024), and denying them to half the section runes is a narrower product than the field names imply.
Surfaced while typing resolveSections' return shape for SPEC-142. The type has to discriminate on emitTag anyway, which is what made the asymmetry visible.
Deliberately not scheduled for v0.38.0: latent (no rune combines the two), minor, and its two fixes are materially different work — making the aliases apply under emitTag adds a resolver capability, while rejecting the combination adds a schema-construction error. Not worth spending that decision to round out a milestone count.
References
- WORK-024 — added
knownSections for validation, aliases and templates - SPEC-142 — typing the transform signature; where the
emitTag discriminant surfaced - ADR-028 — rune identity is not redefinable from outside; why
model should stay ignored here