Skip to content

RFC 015 - Structure, Views, and URIs

This RFC addresses four interrelated requirements for advancing the Vyasa document model:

  1. Views (Profiles): The ability to generate different output variations (e.g., “Reference/Chanting” vs. “Study”) from the same source content.
  2. Whole-Work Processing: Templates that operate on the entire collection (Book/Work) rather than just individual files.
  3. Scoped Metadata: Hierarchical metadata (titles for verse, chapter, canto, work) rather than flat file-scoped variables.
  4. Structural URNs: Constructing URIs based on the document hierarchy (book.canto.chapter) rather than ad-hoc context variables.

A view template defines a body directive that acts as a whitelist and sequencer for document content.

  • Whitelist: Only commands listed in body appear in output. Everything else is implicitly excluded.
  • Sequencer: Commands appear in the order specified in body, regardless of source document order.
  • Inheritance: Child templates (e.g., devanagari inside verse) render normally using base templates from context.vy.

reference.vy (entire file — verses only):

`body [ `verse ]

study.vy (reordered — translation before verse):

`body [
`translation
`verse
`synonyms
]

A .vy file paired with a .html file of the same name forms a view:

  • reference.html — the HTML shell (styles, {{ body }})
  • reference.vy — the body projection (which commands, in what order)
  • context.vy — shared base templates (loaded for all views)

2. Structural Templates (Implicit Layouts)

Section titled “2. Structural Templates (Implicit Layouts)”

The default view (default.vy or context.vy) defines how all content types are rendered. Without a body directive, all commands render in document order.

// context.vy — base templates for all views
`verse [
`div { class="verse-box" } [
`div { class="verse-ref" } [ Verse $.argument ]
`div { class="verse" } [ $.text ]
]
]
`translation [
`div { class="translation-box" } [ $.text ]
]

3. Whole-Work Processing (“Collections”)

Section titled “3. Whole-Work Processing (“Collections”)”
  • Definition: A Collection refers to the aggregated set of all compilation units (individual .vy files) that make up a single “Work” (Book). Currently, Vyasa compiles files in isolation. A “Collection Pass” is a post-compilation step that gathers all these compiled nodes into a single structure (The Work) for generating indices, TOCs, or single-page editions.

Proposed Solution: The Collection Object The compiler performs a second pass where it exposes a work object to a master template (index.vy or work.vy).

User feedback highlights that using block-based commands for chapters (chapter { ... } [ ... ]) is tedious.

We retain the “LaTeX-style” flow commands but improve their state management. Instead of loose variables, chapter updates a dedicated Structural Registry.

  • Location: Often placed in context.vy at the folder root.
  • Mandatory Fields: id and title are required. Missing them is a Compiler Error.
  • Reset Logic: When a new Flow Command (like chapter or canto) is encountered, subordinate counters (like verse) are automatically reset.
# This sets the interaction state.
`chapter { title="Yoga of Despondency" id="1" }
# Notes:
# 1. Pushes new Chapter to Registry.
# 2. Resets Verse counter.
# 3. Validates mandatory 'id' and 'title'.

Nested structures are tedious. Explicit URNs are error-prone.

We define the URN semantics once in configuration. We simplify the placeholders to key names, implying the .id property.

vyasac.toml:

[urn]
corpus = "vedabase.io:sb"
hierarchy = ["canto", "chapter", "verse"]

Content (1.vy):

`chapter { id="1" }
`verse { id="1" } # -> urn:vyasa:bg:1:1:1

Instead of requiring repetitive context.vy files or flow commands in every stream folder just to define the chapter or canto, vyasac infers the hierarchy variables directly from the filesystem.

When a file is compiled (e.g., content/mula/18/78.vy), the compiler parses the relative path to the stream root:

  1. Path 18/78.vy has components ["18", "78.vy"].
  2. The file stem is extracted: ["18", "78"].
  3. These components are aligned with the hierarchy array backwards (since files are leaves):
    • 78 aligns with verse -> sets verse=78 in the document context.
    • 18 aligns with chapter -> sets chapter=18 in the document context.

This makes the directory structure the single source of truth. Users can still use context.vy to provide folder-specific overrides (e.g. `set meta { author="Prabhupada" }) which take precedence over inferred values.

To prevent users from making silent mistakes, the compiler generates warnings and errors during URN evaluation and path inference:

  • Invalid Component Value (Warning): If a path component (e.g., 13-17) is mapped to a hierarchy level but contains characters that violate strict URN formatting rules, a warning is generated.
  • Unresolved URN Variables (Error/Warning): If the URN scheme (e.g., urn:vyasa:bg:{chapter}:{verse}) is evaluated but {chapter} or {verse} cannot be resolved (meaning it wasn’t inferred from the path and wasn’t provided in a context.vy or flow command), the compiler must generate a warning/error instead of silently leaving the literal {chapter} in the final URN.

For addressing content within a compilation unit (e.g., a specific line or anchor):

  • Path vs. Fragment: The Structural URN forms the Path (urn:vyasa:bg:1:1:1). Markers and Segments form the Fragment (#...).
  • Collision Avoidance:
    • Verse ID: Part of the URN Path (...:1).
    • Marker ID: Part of the URN Fragment (...:1#foo). They live in different namespaces and cannot clobber each other.
  • Disambiguation:
    • Segments: Automatically indexed integers (#1, #2).
    • Markers: Explicit string IDs (#foo). If a marker ID is numeric, it shadows the segment index.

When a single compilation unit represents a range of verses (e.g., 13-14.vy):

  • URN Path: The file generates multiple URNs for the verses (...:13, ...:14).
  • URN Fragments: Any Markers or Segments within the content are semantically attached to the last verse in the range (...:14#foo).
    • Rationale: Purports often reference the entire block, and the “anchor” is effectively the end of the sequence.

To maintain strict separation of concerns, the compiler enforces Contextual Validity for commands.

  1. Source Context (book.vy):

    • Allowed: Semantic Commands (verse, h1, p), Flow Commands (chapter), Ad-hoc Markers (mark, |).
    • Forbidden: Presentation Commands (div, span, css classes). Using div in a source file is a Compiler Error.
  2. View Context (template.vy, reference.vy):

    • Allowed: Presentation Commands (div, span), Logic (if, each), Content Accessors ($.text).
    • Purpose: Defining how the semantic nodes are rendered.

We establish a flat convention for HTML generation:

  • The Shell: reference.html (Contains <html>, <head>, scripts, and {{ body }}).
  • The View: reference.vy (Defines the body projection — which commands to include and in what order).
  • Shared Logic: context.vy (Base templates for all views. Loaded automatically; .vy files without a matching .html sibling are base templates).

The compiler automatically pairs them:

  1. Loads context.vy (and any other unpaired .vy files) as base templates.
  2. When reference.html is selected, loads reference.vy as view overrides.
  3. Applies the body projection to filter/reorder document content.
  4. Injects the rendered body into reference.html.

8. Collection Templates (“Whole Work” Processing)

Section titled “8. Collection Templates (“Whole Work” Processing)”

To generate indices, tables of contents, or glossaries, we introduce Collection Templates.

  • Concept: Instead of processing a single content file, the compiler aggregates all compiled documents into a Work object.
  • Usage: vyasac build --collection toc (looks for templates/html/toc.vy).
  • Context: The template receives a global work object containing a flat list or hierarchical tree of all compilation units.

9. Future Work: Segment Recitation Patterns (Ghana/Jata)

Section titled “9. Future Work: Segment Recitation Patterns (Ghana/Jata)”

Oral traditions often require reciting segments in complex combinatorial patterns for memorization (e.g., Jata-patha, Ghana-patha).

  • Requirement: Generate output where segments (identified by |) are permuted according to a pattern.
  • Example: If a verse has segments 1 | 2 | 3:
    • Jata: 1-2-2-1, 2-3-3-2.
  • Design Implication: This will likely require a dedicated transform pass or a specialized view helper (e.g., {{#each (permute segments "1-2-2-1") }} ... {{/each}}) rather than simple static templates.

10. Update (2026-07): Composite URN Anchors & Zero-Bloat Projections

Section titled “10. Update (2026-07): Composite URN Anchors & Zero-Bloat Projections”

When weaving interlinear content across parallel streams (e.g., Devanagari primary text vs. English commentary), a leaf sequence ID (e.g., bg:1:1) represents a Composite URN Anchor across all streams. However, the internal “shape” (sub-block composition) of that anchor varies by stream:

  • mula-deva: Contains a single primary text block (shloka or verse).
  • eng-prabhupada: Contains no verse block, but instead contains three distinct sub-blocks: synonyms, translation, and purport.

To strictly adhere to the publication bloat invariant (with a target not exceeding 25% increase over raw source size), we avoid injecting verbose semantic wrapper attributes (e.g., <div class="vy-subblock vy-synonyms" data-block-type="synonyms">) into every stored row in html_blocks.

  1. Clean Projection Output: Each row in html_blocks stores only the minimal HTML string output produced by the projection template (e.g., <div class="set">...</div><div class="verse">...</div>), preserving structural semantic classes without redundant data attributes.
  2. Runtime Weaving & Sideline Gutters: The viewer (via VyasaViewerRuntime in WASM) joins fragments across selected streams for sequence_id = X. A 3-column grid layout positions the URN sequence anchor in the sticky left gutter, the leaf-block content in the center column, and localized interactive badges (speaker attributions, editorial notes) in the right gutter.
  3. Graph-Based Discovery: Standalone metadata (such as speaker events and editorial notes) is extracted from peer annotations/ directories into graph_nodes and graph_edges during compilation and localized at runtime using the vocabulary table, completely decoupling metadata discovery from textual content storage.