Skip to content

Reference Codec: Wire code examples (including mermaid) through convention extraction pipeline #24

@darko-mijic

Description

@darko-mijic

Context

The reference codec system (createReferenceCodec) composes documents from three content sources: conventions, shapes, and behaviors. Convention content is extracted from decision records tagged with @libar-docs-convention.

Problem: parseBusinessRuleAnnotations() in helpers.ts already extracts code examples (including """mermaid""" blocks) into annotations.codeExamples as SectionBlock[]. However, extractConventionRuleContent() in convention-extractor.ts discards this data because ConventionRuleContent has no codeExamples field.

Evidence: ADR-006 (Process Guard) contains two mermaid diagrams (stateDiagram-v2 and flowchart LR) in convention-tagged Rule blocks. The generated PROCESS-GUARD-REFERENCE.md shows empty gaps where these diagrams should appear — only the surrounding narrative text survives.

Data flow showing the loss point

Rule description with """mermaid ... """
  │
  ├─ Path A: ADR/Decision Doc Codec
  │    └─ parseDescriptionWithDocStrings() → code(content, "mermaid") ✅ preserved
  │
  └─ Path B: Convention Extractor → Reference Codec
       ├─ parseBusinessRuleAnnotations()
       │    ├─ codeExamples: [code("stateDiagram-v2\n...", "mermaid")]  ✅ extracted
       │    └─ remainingContent: "The FSM enforces..."
       │
       └─ extractConventionRuleContent()
            ├─ narrative = remainingContent
            └─ codeExamples → DISCARDED  ← loss point

Proposed Changes

1. Add codeExamples to ConventionRuleContent (convention-extractor.ts)

import type { SectionBlock } from '../schema.js';

export interface ConventionRuleContent {
  // ...existing fields...

  /** Code examples extracted from DocStrings in the rule description (includes mermaid diagrams) */
  readonly codeExamples?: readonly SectionBlock[];
}

Wire in extractConventionRuleContent():

function extractConventionRuleContent(rule: BusinessRule): ConventionRuleContent {
  const annotations = parseBusinessRuleAnnotations(rule.description);
  const tables = extractTablesFromDescription(rule.description);

  return {
    ruleName: rule.name,
    ...(annotations.invariant !== undefined && { invariant: annotations.invariant }),
    ...(annotations.rationale !== undefined && { rationale: annotations.rationale }),
    ...(annotations.verifiedBy !== undefined && { verifiedBy: annotations.verifiedBy }),
    tables,
    narrative: annotations.remainingContent ?? '',
    ...(annotations.codeExamples && annotations.codeExamples.length > 0 && {
      codeExamples: annotations.codeExamples,
    }),
  };
}

2. Emit code/mermaid blocks in buildConventionSections() (reference.ts)

Import mermaid from schema, then render codeExamples after tables:

import {
  // ...existing imports...
  mermaid,
} from '../schema.js';

// In buildConventionSections(), after table rendering, before separator:
if (rule.codeExamples && detailLevel !== 'summary') {
  for (const example of rule.codeExamples) {
    if (example.type === 'code' && 'language' in example && example.language === 'mermaid') {
      sections.push(mermaid((example as { content: string }).content));
    } else {
      sections.push(example);
    }
  }
}

Note: parseBusinessRuleAnnotations produces code(content, "mermaid") (CodeBlock with language: "mermaid"). We convert these to mermaid() blocks (MermaidBlock) for semantic correctness — both render identically as ```mermaid ``` but MermaidBlock is the canonical type for programmatic consumers (e.g., future SVG rendering per #16).

3. Test scenarios

Convention extractor (convention-extractor.feature):

Rule: Code examples in rule descriptions are preserved

  @happy-path
  Scenario: Mermaid diagram in rule description is extracted as code example
    Given a pattern with convention "fsm-rules" and rule description:
      """
      The FSM enforces valid state transitions.

      """mermaid
      stateDiagram-v2
          [*] --> roadmap
          roadmap --> active
      """
      """
    When extracting conventions for tag "fsm-rules"
    Then the first rule has 1 code example
    And the code example has language "mermaid"

Reference codec (reference-codec.feature):

Rule: Convention code examples render as mermaid blocks

  @happy-path
  Scenario: Convention with mermaid content produces mermaid block in output
    Given a reference config with convention tags "fsm-rules" and behavior tags ""
    And a MasterDataset with a convention pattern with a mermaid diagram
    When decoding at detail level "detailed"
    Then the document contains a mermaid block

  @happy-path
  Scenario: Summary level omits code examples
    Given a reference config with convention tags "fsm-rules" and behavior tags ""
    And a MasterDataset with a convention pattern with a mermaid diagram
    When decoding at detail level "summary"
    Then the document does not contain a mermaid block

Verification

After implementation:

  1. pnpm test — all existing + new tests pass
  2. pnpm docs:reference — regenerate reference docs
  3. Check docs-generated/docs/PROCESS-GUARD-REFERENCE.md — the "FSM Diagram" section should now contain the stateDiagram-v2 mermaid block, and the "Architecture" section should contain the flowchart LR diagram

Why This Matters

This is a prerequisite for generating rich architecture overview documents in the consuming monorepo. The reference codec is the right vehicle for composing multi-diagram architecture docs (convention narrative + mermaid diagrams + TypeScript shapes), but currently silently drops all diagram content from convention sources.

Once this is in place, a new reference config (12th entry) can produce architecture overview documents with interleaved mermaid diagrams and explanatory copy — similar to what currently requires hand-maintained PR descriptions.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions