Storytelling Package Pipeline Hooks
Summary
The @refrakt-md/storytelling package defines 7 rune types (character, realm, faction, lore, plot, bond, storyboard) but has no pipelineHooks. Storytelling entities are invisible to the cross-page pipeline — bold character names can't auto-link to character pages, bonds can't resolve their from/to targets, and there's no relationship map data.
Add pipeline hooks that register storytelling entities, build a relationship graph from bonds, and resolve bold-text cross-links during post-processing.
Acceptance Criteria
- Storytelling package exports
pipelineHookswithregister,aggregate, andpostProcesshooks registerhook registers entities for: character, realm, faction, lore, plot, bond- Each entity includes: name, type, source page URL, and type-specific metadata (role, spoiler level, etc.)
aggregatehook builds a relationship graph from bond entitiesaggregatehook validates bond references — warns on orphaned bonds (from/to entity doesn't exist)postProcesshook resolves bold text (**Veshra**) to cross-page links when the name matches a registered entity- Cross-links only resolve to other pages (not self-links on the entity's own page)
- Cross-links resolve first occurrence per page only (avoid over-linking)
- Cross-links don't resolve inside headings, code blocks, or other runes
- Tests cover entity registration, bond validation, and cross-link resolution
Approach
Follow the pattern established by planPipelineHooks in runes/plan/src/pipeline.ts. Walk each page's renderable tree looking for nodes with typeof matching storytelling schema types (Character, Realm, Faction, Lore, Plot, Bond). Extract the name property and register entities.
For bold-text cross-linking in postProcess, walk the AST looking for strong nodes. Extract their text content, check against the registry, and wrap in a link if a match exists on a different page.
References
- Cross-Page Pipeline — Specification (Cross-Page Pipeline — Pattern 1: Reference Resolution)
- Community Runes — Specification (Community Runes —
@refrakt-md/storytelling)
Resolution
Branch: claude/work-item-054-aBDgF PR: refrakt-md/refrakt#130
What was done
- Created
runes/storytelling/src/pipeline.tswith all three pipeline hooks registerhook identifies runes viadata-runeattribute, reads entity names fromdata-namerefs and metadata fromdata-fieldmeta tags (using kebab-case conversion matching the identity transform engine)aggregatehook builds entity-by-name map including character aliases, constructs bidirectional relationship graph from bonds, and emits warnings for orphaned bond referencespostProcesshook walks the renderable tree, wrapping matchingstrongtags in<a>links to entity pages; tracks linked names per page for first-occurrence-only behavior; skips headings, code blocks, and nested rune containers- Wired hooks into the
RunePackageexport viapipelinefield inindex.ts - Added 19 tests covering all acceptance criteria (64 total storytelling tests passing)
Notes
- Property names are stored as kebab-case
data-fieldattributes after identity transform (e.g.,bondType→bond-type), so the pipeline'sreadFieldhelper appliestoKebabCaseconversion - Character aliases are registered as additional entity-by-name entries pointing to the same entity registration, enabling cross-links via alternate names (e.g., "Strider" → Aragorn's page)
Relationships
History
- 59ded4aResolution recorded
- f262d7bsource+SPEC-001,SPEC-002
- 753243estatusin-progress→done
- ☑ Storytelling package exports `pipelineHooks` with `register`, `aggregate`, and `postProcess` hooks
- ☑ `register` hook registers entities for: character, realm, faction, lore, plot, bond
- ☑ Each entity includes: name, type, source page URL, and type-specific metadata (role, spoiler level, etc.)
- +7 more criteria
- ae0dbcbstatusready→in-progress
- a129a9emilestone+v0.9.0
- 2f24c14Created (ready, high, moderate, storytelling, pipeline)