Skip to content

Personas API

The fcc.personas package contains models, registry, cross-reference matrix, and dimension profiles for the 24 FCC personas across 5 categories.

classDiagram
    class PersonaSpec {
        +str id
        +str name
        +str fcc_phase
        +str category
        +RISCEARSpec riscear
        +str champion_of
        +list orchestrates
    }
    class RISCEARSpec {
        +str role
        +list inputs
        +str style
        +list constraints
        +list expected_output
        +str archetype
        +list responsibilities
    }
    class PersonaRegistry {
        +get(id) PersonaSpec
        +by_category(cat) list
        +champions() list
        +merge(other) PersonaRegistry
    }
    class CrossReferenceMatrix {
        +upstream(id) list
        +downstream(id) list
        +by_type(type) list
        +merge(other) CrossReferenceMatrix
    }
    PersonaSpec *-- RISCEARSpec : contains
    PersonaRegistry o-- PersonaSpec : manages
    CrossReferenceMatrix ..> PersonaSpec : references

RISCEARSpec

The 10-component R.I.S.C.E.A.R. specification defines the behavioral contract for each persona: Role, Input, Style, Constraints, Expected Output, Archetype, Responsibilities, Role Skills, Role Collaborators, and Role Adoption Checklist.

from fcc.personas.models import RISCEARSpec

spec = RISCEARSpec(
    role="Senior analyst responsible for structured research.",
    inputs=["stakeholder requirements", "domain literature"],
    style="Methodical, evidence-based, citation-rich.",
    constraints=["Must cite sources", "No speculation"],
    expected_output=["Research inventory", "Gap analysis"],
    archetype="The Scholar",
    responsibilities=["Gather primary sources", "Synthesize findings"],
    role_skills=["Literature review", "Data analysis"],
    role_collaborators=["Blueprint Crafter", "Documentation Evangelist"],
    role_adoption_checklist=["Review style guide", "Set up citation manager"],
)

print(spec.role)          # "Senior analyst responsible ..."
print(spec.role_skills)   # ["Literature review", "Data analysis"]

Constructing from a Dict

data = {
    "role": "Senior analyst responsible for structured research.",
    "inputs": ["stakeholder requirements"],
    "style": "Methodical",
    "constraints": ["Must cite sources"],
    "expected_output": ["Research inventory"],
    "archetype": "The Scholar",
    "responsibilities": ["Gather primary sources"],
    "role_skills": ["Literature review"],
    "role_collaborators": ["Blueprint Crafter"],
    "role_adoption_checklist": ["Review style guide"],
}
spec = RISCEARSpec.from_dict(data)

PersonaSpec

The top-level model for an FCC persona. All fields are immutable (frozen dataclass). Each persona belongs to a category (core, integration, governance, stakeholder, champion) and is assigned to an FCC phase (Find, Create, Critique, All, or Orchestration).

from fcc.personas.models import PersonaSpec, RISCEARSpec

persona = PersonaSpec(
    id="RC",
    name="Research Crafter",
    fcc_phase="Find",
    role_title="Senior Research Analyst",
    category="core",
    color="#4CAF50",
    riscear=RISCEARSpec(
        role="Research synthesis and inventory building.",
        inputs=["stakeholder requirements"],
        style="Methodical",
        constraints=["Cite sources"],
        expected_output=["Research inventory"],
        archetype="The Scholar",
        responsibilities=["Gather sources"],
    ),
)

print(persona.id)            # "RC"
print(persona.name)          # "Research Crafter"
print(persona.fcc_phase)     # "Find"
print(persona.category)      # "core"
print(persona.riscear.role)  # "Research synthesis and inventory building."

Champion Personas

Champions are elevated personas that coordinate teams of base personas.

# A champion persona references a base persona and a list of orchestrated IDs
champion = PersonaSpec(
    id="RCHM",
    name="Research Champion",
    fcc_phase="Orchestration",
    role_title="Research Orchestration Lead",
    category="champion",
    champion_of="RC",               # base persona this champions
    orchestrates=["RC", "CIA", "RIC"],  # IDs of coordinated personas
    riscear=RISCEARSpec(
        role="Orchestrate research team.",
        inputs=["Team deliverables"],
        style="Directive",
        constraints=[],
        expected_output=["Coordination report"],
        archetype="The General",
        responsibilities=["Coordinate team"],
    ),
)

print(champion.champion_of)   # "RC"
print(champion.orchestrates)   # ["RC", "CIA", "RIC"]

Discernment Matrix and Design Target Factors

Each persona may carry up to 6 discernment traits and 6 design target factors, each with 7 rating dimensions.

from fcc.personas.models import (
    DiscernmentTrait,
    DesignTargetFactor,
    RatingDimensions,
)

trait = DiscernmentTrait(
    name="Curiosity",
    description="Drive to explore unfamiliar domains.",
    ratings=RatingDimensions(self_rating=4.2, peer_rating=4.5),
)

factor = DesignTargetFactor(
    name="Optimism",
    description="Belief that obstacles can be overcome.",
    ratings=RatingDimensions(self_rating=3.8, org_rating=4.0),
)

# Serialize to dict
print(trait.to_dict())
# {'name': 'Curiosity', 'description': 'Drive to ...', 'ratings': {'self_rating': 4.2, 'peer_rating': 4.5}}

PersonaRegistry

The registry is the primary entry point for loading personas. It supports loading from a single YAML file, a directory of YAML files, or explicit file paths, with optional JSON Schema validation.

Loading from the Bundled Data

from fcc._resources import get_personas_dir, get_schemas_dir
from fcc.personas.registry import PersonaRegistry

# Load all 24 personas from the bundled data directory
registry = PersonaRegistry.from_yaml_directory(get_personas_dir())
print(f"Total personas: {len(registry)}")  # 24

Loading from a Single File with Validation

from fcc._resources import get_personas_dir, get_schemas_dir
from fcc.personas.registry import PersonaRegistry

registry = PersonaRegistry.from_yaml_validated(
    yaml_path=get_personas_dir() / "core.yaml",
    schema_path=get_schemas_dir() / "persona.schema.json",
)
print(f"Core personas: {len(registry)}")  # 5

Loading from Multiple Files

from fcc._resources import get_personas_dir
from fcc.personas.registry import PersonaRegistry

registry = PersonaRegistry.from_yaml_files(
    get_personas_dir() / "core.yaml",
    get_personas_dir() / "integration.yaml",
)
print(f"Core + integration: {len(registry)}")  # 12

Lookup Methods

from fcc._resources import get_personas_dir
from fcc.personas.registry import PersonaRegistry

registry = PersonaRegistry.from_yaml_directory(get_personas_dir())

# By short ID
rc = registry.get("RC")
print(rc.name)  # "Research Crafter"

# By full name
bc = registry.get_by_name("Blueprint Crafter")
print(bc.id)  # "BC"

# By FCC phase
find_personas = registry.by_phase("Find")
print([p.id for p in find_personas])

# By category
gov_personas = registry.by_category("governance")
print([p.name for p in gov_personas])

# All champions
champs = registry.champions()
print([p.id for p in champs])  # ["RCHM", "BCHM", "UGCH", "RBCH"]

# Find the champion for a base persona
rc_champion = registry.champion_of("RC")
print(rc_champion.id)  # "RCHM"

# Resolve a champion's base persona
base = registry.base_of("RCHM")
print(base.id)  # "RC"

Iterating and Checking Membership

# Iterate over all personas
for persona in registry:
    print(f"{persona.id}: {persona.name} ({persona.category})")

# Check if an ID exists
print("RC" in registry)  # True

# List all IDs and names
print(registry.ids)    # ["RC", "BC", "DE", ...]
print(registry.names)  # ["Research Crafter", "Blueprint Crafter", ...]

Merging Registries

from fcc.personas.registry import PersonaRegistry

core_reg = PersonaRegistry.from_yaml(get_personas_dir() / "core.yaml")
ext_reg = PersonaRegistry.from_yaml(get_personas_dir() / "integration.yaml")

combined = core_reg.merge(ext_reg)
print(f"Combined: {len(combined)}")

CrossReferenceMatrix

The cross-reference matrix captures persona-to-persona interactions with typed relationships: handoff, feedback, coordination, governance, and champion-of.

Loading from YAML

from fcc._resources import get_personas_dir
from fcc.personas.cross_reference import CrossReferenceMatrix

matrix = CrossReferenceMatrix.from_yaml(
    get_personas_dir() / "cross_reference.yaml"
)
print(f"Total entries: {len(matrix)}")
from fcc._resources import get_personas_dir
from fcc.personas.registry import PersonaRegistry
from fcc.personas.cross_reference import CrossReferenceMatrix

registry = PersonaRegistry.from_yaml_directory(get_personas_dir())
matrix = CrossReferenceMatrix.from_personas(registry)
print(f"Auto-generated entries: {len(matrix)}")

Querying the Matrix

from fcc._resources import get_personas_dir
from fcc.personas.cross_reference import CrossReferenceMatrix

matrix = CrossReferenceMatrix.from_yaml(
    get_personas_dir() / "cross_reference.yaml"
)

# Who sends work to RC?
for entry in matrix.upstream("RC"):
    print(f"  {entry.source_id} -> RC: {entry.interaction}")

# Who does RC hand off to?
for entry in matrix.downstream("RC"):
    print(f"  RC -> {entry.target_id}: {entry.interaction}")

# Coordination peers of DE
for entry in matrix.peers("DE"):
    other = entry.target_id if entry.source_id == "DE" else entry.source_id
    print(f"  DE <-> {other}: {entry.interaction}")

# All handoff relationships
handoffs = matrix.by_type("handoff")
print(f"Total handoffs: {len(handoffs)}")

# Direct interactions between two specific personas
entries = matrix.between("RC", "BC")
for e in entries:
    print(f"  {e.source_id} -> {e.target_id}: {e.relationship_type}")

# All persona IDs referenced in the matrix
all_ids = matrix.all_persona_ids()
print(f"Personas in matrix: {sorted(all_ids)}")

Merging Matrices

from fcc.personas.cross_reference import CrossReferenceMatrix

yaml_matrix = CrossReferenceMatrix.from_yaml(
    get_personas_dir() / "cross_reference.yaml"
)
auto_matrix = CrossReferenceMatrix.from_personas(registry)

# Simple merge (may have duplicates)
combined = yaml_matrix.merge(auto_matrix)

# Merge with precedence (yaml_matrix entries win on key conflicts)
combined = yaml_matrix.merge_with_precedence(auto_matrix)

Adding Entries and Writing YAML

from fcc.personas.cross_reference import CrossReferenceEntry, CrossReferenceMatrix

matrix = CrossReferenceMatrix()
matrix.add(CrossReferenceEntry(
    source_id="RC",
    target_id="BC",
    relationship_type="handoff",
    interaction="Research inventory delivered to Blueprint Crafter",
    strength="primary",
))

matrix.to_yaml("/tmp/my_cross_refs.yaml")

DimensionRegistry

The dimension registry holds the canonical 9-category, 56-dimension specification that defines the full breadth of persona attributes.

Loading and Inspecting

from fcc.personas.dimensions import DimensionRegistry

# Load from a YAML file with dimension definitions
dim_reg = DimensionRegistry.from_yaml("path/to/dimensions.yaml")

print(f"Total dimensions: {dim_reg.total_dimensions}")  # 56
print(f"Categories: {dim_reg.categories}")
# ['core_persona_elements', 'behavioral_and_motivational_factors', ...]

# List all dimension names
for name in dim_reg.dimension_names():
    print(f"  {name}")

# Get dimensions for a specific category
core_dims = dim_reg.dimensions_for_category("core_persona_elements")
for dim in core_dims:
    print(f"  {dim.name}: {dim.description}")
    for attr in dim.attributes:
        print(f"    - {attr.name}: {attr.value}")

PersonaDimensionProfile

A persona's dimension profile captures its specific values across all 9 categories. Profiles are loaded automatically by PersonaRegistry.from_yaml_directory when a dimensions/ subdirectory exists under the personas data directory.

from fcc._resources import get_personas_dir
from fcc.personas.registry import PersonaRegistry

registry = PersonaRegistry.from_yaml_directory(get_personas_dir())
rc = registry.get("RC")

if rc.dimension_profile:
    profile = rc.dimension_profile
    print(f"Total dimensions: {profile.total_dimensions}")
    print(f"Populated categories: {profile.populated_categories}")

    for cat in profile.populated_categories:
        dims = profile.dimensions_for_category(cat)
        print(f"\n{cat} ({len(dims)} dimensions):")
        for dim in dims:
            print(f"  {dim.name}: {dim.description}")