Skip to content

Custom Documentation Templates

This tutorial covers the FCC Jinja2 template system: how templates work, what variables are available, how to create new templates, and how to integrate them with the DocGenerator.

How the Template System Works

FCC uses Jinja2 templates to generate documentation files for each persona. The DocGenerator class loads templates from a templates directory, renders them with persona-specific context, and writes the output as Markdown files.

The built-in templates live at src/fcc/templates/docs/ and produce a comprehensive documentation suite for each persona:

Template Output Purpose
persona/index.md.j2 index.md Persona landing page
persona/specification.md.j2 specification.md Full R.I.S.C.E.A.R. specification
persona/constitution.md.j2 constitution.md Constitution rules (hard_stop, mandatory, preferred)
persona/coordination.md.j2 coordination.md Collaboration map and handoff details
persona/prompts/index.md.j2 prompts/index.md Prompt catalog index
persona/prompts/prompt.md.j2 prompts/{id}.md Individual prompt pages
persona/tutorials/index.md.j2 tutorials/index.md Tutorial catalog index
persona/tutorials/tutorial.md.j2 tutorials/{id}.md Individual tutorial pages
persona/workflows/index.md.j2 workflows/index.md Workflow catalog index
persona/workflows/workflow.md.j2 workflows/{id}.md Individual workflow pages
persona/package/index.md.j2 package/index.md Package summary
cross-reference/matrix.md.j2 Cross-reference matrix Persona interaction map
cross-reference/coordination-methods.md.j2 Coordination methods How personas coordinate
cross-reference/constitution-framework.md.j2 Constitution framework Shared governance rules
sitemap.md.j2 SITEMAP.md Full documentation sitemap

Template Variables

When rendering a persona template, the DocGenerator provides these context variables:

Core Variables

Variable Type Description
persona PersonaSpec The full persona specification object
persona.id str Short ID (e.g., "RC")
persona.name str Full name (e.g., "Research Crafter")
persona.fcc_phase str FCC phase (Find, Create, Critique, All, Orchestration)
persona.role_title str Professional title
persona.color str Hex color code
persona.category str Category (core, integration, governance, stakeholder, champion)
persona.riscear RISCEARSpec Full R.I.S.C.E.A.R. specification
persona.deliverables list Named deliverables with descriptions
persona.collaboration list Collaboration relationships

R.I.S.C.E.A.R. Variables

Access via persona.riscear:

Variable Type
persona.riscear.role str
persona.riscear.inputs list[str]
persona.riscear.style str
persona.riscear.constraints list[str]
persona.riscear.expected_output list[str]
persona.riscear.archetype str
persona.riscear.responsibilities list[str]
persona.riscear.role_skills list[str]
persona.riscear.role_collaborators list[str]
persona.riscear.role_adoption_checklist list[str]

Documentation Context Variables

Variable Type Description
doc_context dict Documentation generation context
doc_context.primary_action str Primary action verb
doc_context.primary_output str Primary output type
doc_context.constitution dict Constitution rules
default_constitution dict Shared default constitution
tutorial_count int Number of tutorials generated
prompt_count int Number of prompts generated

Creating a New Template

Step 1: Create the Template File

Create a Jinja2 template with .j2 extension:

my_project/templates/docs/persona/security-review.md.j2

Example template content:

---
title: "Security Review - {{ persona.name }}"
---

# Security Review: {{ persona.name }}

## Overview

This document provides security review guidelines for the **{{ persona.name }}** ({{ persona.id }}) persona.

**Phase:** {{ persona.fcc_phase }}
**Archetype:** {{ persona.riscear.archetype }}

## Constraints

{% for constraint in persona.riscear.constraints %}
- {{ constraint }}
{% endfor %}

## Constitution Rules

### Hard Stops
{% if doc_context and doc_context.constitution %}
{% for rule in doc_context.constitution.hard_stop %}
- {{ rule }}
{% endfor %}
{% endif %}

### Mandatory
{% if doc_context and doc_context.constitution %}
{% for rule in doc_context.constitution.mandatory %}
- {{ rule }}
{% endfor %}
{% endif %}

## Deliverables

| Deliverable | Description |
|---|---|
{% for d in persona.deliverables %}
| {{ d.name }} | {{ d.description }} |
{% endfor %}

## Collaboration

{% for c in persona.collaboration %}
- **{{ c.target }}** ({{ c.direction }}): {{ c.interaction }}
{% endfor %}

Step 2: Use Jinja2 Features

FCC templates support standard Jinja2 features:

{# Comments #}

{# Conditionals #}
{% if persona.champion_of %}
This persona is a champion of {{ persona.champion_of }}.
{% endif %}

{# Loops with index #}
{% for skill in persona.riscear.role_skills %}
{{ loop.index }}. {{ skill }}
{% endfor %}

{# Filters #}
{{ persona.name | upper }}
{{ persona.riscear.role | truncate(100) }}

{# Default values #}
{{ doc_context.get('primary_action', 'documentation') }}

Integrating with DocGenerator

Option 1: Override the Templates Directory

Point the DocGenerator to your custom templates directory:

from pathlib import Path
from fcc.personas.registry import PersonaRegistry
from fcc.scaffold.doc_generator import DocGenerator

registry = PersonaRegistry.from_yaml_directory("data/personas/")

generator = DocGenerator(
    registry=registry,
    templates_dir=Path("my_project/templates/"),
)

# Generate docs using your custom templates
count = generator.generate_all_docs(Path("output/docs"))
print(f"Generated {count} files")

Your custom templates directory should mirror the structure of the built-in templates:

my_project/templates/
  docs/
    persona/
      index.md.j2
      specification.md.j2
      security-review.md.j2    # Your custom template
    cross-reference/
      matrix.md.j2
    sitemap.md.j2

Option 2: Extend the DocGenerator

For more control, subclass DocGenerator and add custom rendering:

from fcc.scaffold.doc_generator import DocGenerator

class CustomDocGenerator(DocGenerator):
    def generate_persona_docs(self, persona_id, output_dir):
        # Call parent to generate standard docs
        count = super().generate_persona_docs(persona_id, output_dir)

        # Add custom security review
        persona = self.registry.get(persona_id)
        slug = persona.id.lower()
        base = output_dir / "fcc" / "personas" / persona.category / slug
        doc_ctx = persona.doc_context or {}

        self._write(
            "docs/persona/security-review.md.j2",
            base / "security-review.md",
            {"persona": persona, "doc_context": doc_ctx},
        )
        count += 1

        return count

Template Data Sources

The DocGenerator loads additional data from YAML files in the data/docs/ directory:

File Purpose Used By
tutorial_topics.yaml Tutorial titles and outcomes per difficulty level Tutorial templates
prompt_topics.yaml Prompt titles and descriptions per difficulty level Prompt templates
constitution_template.yaml Default constitution rules and workflow definitions Constitution and workflow templates

You can provide custom versions of these files via the data_dir parameter:

generator = DocGenerator(
    registry=registry,
    templates_dir=Path("my_project/templates/"),
    data_dir=Path("my_project/data/"),
)

Output Structure

The DocGenerator produces a structured output directory:

output/docs/fcc/personas/
  core/
    rc/
      index.md
      specification.md
      constitution.md
      coordination.md
      prompts/
        index.md
        prompt-001.md
        ...
      tutorials/
        index.md
        tutorial-001.md
        ...
      workflows/
        index.md
        ...
      package/
        index.md
  integration/
    cia/
      ...
  governance/
    ...
  stakeholder/
    ...
  champion/
    ...
  cross-reference-matrix.md
  coordination-methods.md
  constitution-framework.md
  SITEMAP.md

Each persona gets approximately 56 files. With 24 personas plus cross-reference and sitemap files, the full generation produces approximately 1,348 documentation files.

Next Steps