Data Model: Agent Profile System

Phase 1 output for feature 045-agent-profile-system

Entities

AgentProfile (existing — src/doctrine/agent_profiles/profile.py)

FieldTypeRequiredDescription
profile_idstr (kebab-case)YesUnique identifier
namestrYesDisplay name
purposestrYesAgent's purpose statement
roleRoleNoRole enum or custom string
routing_priorityint (0-100)NoMatching priority, default 50
max_concurrent_tasksint (>=1)NoWorkload limit
specializes_fromstrNoParent profile ID for inheritance
context_sourcesContextSourcesNoDoctrine references
specializationSpecializationNoFocus areas and boundaries
specialization_contextSpecializationContextNoLanguage/framework/file matching criteria
collaborationCollaborationContractNoPartner roles and handoff protocols
mode_defaultsModeDefaultsNoAutonomy, communication, error handling
initializationInitializationDeclarationNoStartup greeting and context loading

Identity: profile_id (globally unique within shipped + project scope) Lifecycle: Immutable at shipped level; project profiles can be created/updated/deleted State transitions: None (profiles are configuration, not stateful entities)

Role (existing — src/doctrine/agent_profiles/profile.py)

StrEnum with values: implementer, reviewer, architect, designer, planner, researcher, curator, manager

Custom strings accepted with warning via _missing_() classmethod.

ContextSources (existing — value object)

FieldTypeDescription
paradigmslist[str]Referenced paradigm IDs
directiveslist[DirectiveRef]Referenced directive codes + names
tacticslist[str]Referenced tactic IDs

DirectiveRef (existing — value object)

FieldTypeDescription
codestrDirective code (e.g., "004")
namestrDirective display name

Specialization (existing — value object)

FieldTypeDescription
primary_focusstrMain area of expertise
secondary_awarenesslist[str]Adjacent competencies
avoidance_boundarylist[str]Explicitly out-of-scope areas

SpecializationContext (existing — value object)

FieldTypeDescription
languageslist[str]Programming languages
frameworkslist[str]Frameworks/libraries
file_patternslist[str]Glob patterns (e.g., */.py)
keywordslist[str]Matching keywords

CollaborationContract (existing — value object)

FieldTypeDescription
partnerslist[PartnerRole]Collaboration partners with protocols

ModeDefaults (existing — value object)

FieldTypeDescription
autonomy_levelstr"low" / "medium" / "high"
communication_stylestrCommunication preference
error_handlingstrError response strategy

InitializationDeclaration (existing — value object)

FieldTypeDescription
greetingstrStartup message
context_loadinglist[str]Context items to load

ToolConfig (existing — src/specify_cli/core/tool_config.py)

FieldTypeDescription
availablelist[str]Tool identifiers (e.g., "claude", "codex")
selectionToolSelectionConfigRole-based tool preferences

YAML key: tools (renamed from agents in WP08, backward-compat fallback reads agents)

Directive (existing schema — src/doctrine/schemas/directive.schema.yaml)

FieldTypeRequiredDescription
schema_versionstr ("1.0")YesSchema version
idstr (UPPER_SNAKE)YesDirective identifier
titlestrYesDisplay title
intentstrYesGovernance intent statement
tactic_refslist[str]NoReferenced tactic IDs
enforcementstr ("required"/"advisory")YesEnforcement level

TaskContext (existing — src/doctrine/agent_profiles/profile.py)

FieldTypeDescription
languagestrPrimary programming language
frameworkstrPrimary framework
complexitystr"low" / "medium" / "high"
file_patternslist[str]Files involved
keywordslist[str]Task-related keywords
current_tasksintAgent's current workload
profile_idstrSpecific profile request (optional)

Relationships

AgentProfile
  ├── has-one Role
  ├── has-one ContextSources
  │     ├── references-many Directive (via DirectiveRef.code)
  │     ├── references-many Paradigm (by ID)
  │     └── references-many Tactic (by ID)
  ├── has-one Specialization
  ├── has-one SpecializationContext
  ├── has-one CollaborationContract
  ├── has-one ModeDefaults
  ├── has-one InitializationDeclaration
  └── specializes-from AgentProfile (self-referential, optional)

AgentProfileRepository
  ├── loads-from shipped/ (immutable package data)
  ├── loads-from .kittify/constitution/agents/ (project overrides)
  ├── merges profiles (two-source: project overrides shipped)
  ├── resolves inheritance (shallow merge up ancestor chain) [WP15]
  └── matches TaskContext → AgentProfile (weighted scoring)

ToolConfig
  ├── reads-from .kittify/config.yaml (tools key)
  └── referenced-by AgentProfileRepository (for tool detection in init)

Mission (schema update in WP14)
  ├── has-many states/steps
  └── each state/step optionally references AgentProfile (by profile-id)

Merge Semantics

Two-Source Merge (existing — shipped + project)

When a project profile has the same profile-id as a shipped profile:

  • Project fields override shipped fields at the top level
  • Unspecified project fields fall through from shipped

Inheritance Merge (new — WP15)

When a profile declares specializes-from: 1. Walk ancestor chain to root (cycle-safe via validate_hierarchy()) 2. Start from root ancestor profile 3. For each descendant in chain, shallow-merge on top:

4. Return fully resolved profile

  • For each section (dict): child keys override parent keys one level deep
  • Parent keys absent from child are preserved
  • Scalar fields: child value replaces parent value

Example:

Parent (implementer):
  specialization-context:
    languages: [python, javascript]
    frameworks: [django]
  mode-defaults:
    autonomy-level: medium
    communication-style: structured

Child (python-pedro, specializes-from: implementer):
  specialization-context:
    languages: [python]          # overrides parent's languages
  mode-defaults:
    autonomy-level: high         # overrides parent's autonomy-level

Resolved:
  specialization-context:
    languages: [python]          # from child
    frameworks: [django]         # inherited from parent
  mode-defaults:
    autonomy-level: high         # from child
    communication-style: structured  # inherited from parent