Skip to content

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

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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions