Quickstart: Feature 048 Implementation Reference

Feature: Structured Agent Identity & Constitution-Profile Integration

At a Glance

TrackScopeKey Files
A — Identity (WP01–03)ActorIdentity dataclass, frontmatter, CLIidentity.py, models.py, transitions.py, frontmatter.py, tasks.py
B — Constitution (WP04–07)Catalog expansion, transitive resolver, compiler wiringcatalog.py, reference_resolver.py, compiler.py, resolver.py
Convergence (WP08)Integration teststests/integration/

WP Implementation Cheat Sheet

WP01: ActorIdentity Dataclass

# src/specify_cli/identity.py (NEW)
@dataclass(frozen=True)
class ActorIdentity:
    tool: str
    model: str = "unknown"
    profile: str = "unknown"
    role: str = "unknown"

    @classmethod
    def from_compact(cls, s: str) -> ActorIdentity: ...
    @classmethod
    def from_legacy(cls, s: str) -> ActorIdentity: ...
    def to_compact(self) -> str: ...
    def to_dict(self) -> dict[str, str]: ...
    @classmethod
    def from_dict(cls, d: dict[str, str]) -> ActorIdentity: ...

# Modify StatusEvent in src/specify_cli/status/models.py
# actor: str → actor: ActorIdentity
# Update to_dict()/from_dict() for backwards compat

WP02: Frontmatter Structured Agent

# src/specify_cli/frontmatter.py — update extract_scalar for agent field
# Read: str → ActorIdentity.from_legacy(); dict → ActorIdentity.from_dict()
# Write: always structured YAML mapping

# src/specify_cli/tasks_support.py — WorkPackage.agent
# Return type: str | None → ActorIdentity | None

WP03: CLI Flags

# src/specify_cli/identity.py — add parser
def parse_agent_identity(
    agent: str | None,
    tool: str | None, model: str | None,
    profile: str | None, role: str | None,
) -> ActorIdentity | None:
    # Mutual exclusion: raise if both agent and any individual flag provided
    ...

# Add --tool/--model/--profile/--role to:
# - src/specify_cli/cli/commands/agent/tasks.py (move-task)
# - src/specify_cli/cli/commands/agent/workflow.py (implement, review)

WP04: DoctrineCatalog Expansion

# src/specify_cli/constitution/catalog.py
# Add: tactics, styleguides, toolguides, procedures, profiles frozensets
# Reuse _load_yaml_id_catalog() with appropriate glob patterns

WP05: Transitive Reference Resolver

# src/specify_cli/constitution/reference_resolver.py (NEW)
def resolve_references_transitively(
    directive_ids: list[str],
    doctrine_service: DoctrineService,
) -> ResolvedReferenceGraph:
    # DFS with visited set (cf. engine.py:depth_first_order pattern)
    ...

WP06: Compiler + DoctrineService

# src/specify_cli/constitution/compiler.py
def compile_constitution(
    *, mission, interview, template_set=None,
    doctrine_catalog=None,
    doctrine_service=None,  # NEW optional param
) -> CompiledConstitution:
    if doctrine_service is not None:
        # Use transitive resolution via DoctrineService
        ...
    else:
        # Fallback: existing _index_yaml_assets() path
        diagnostics.append("DoctrineService unavailable; using YAML scanning fallback")

WP07: Profile-Aware Governance

# src/specify_cli/constitution/resolver.py
def resolve_governance_for_profile(
    profile_id: str, role: str | None,
    doctrine_service: DoctrineService,
    interview: ConstitutionInterview,
) -> GovernanceResolution:
    # 1. Load profile via doctrine_service.agent_profiles.resolve_profile()
    # 2. Extract directive_references from profile
    # 3. Merge with interview.selected_directives (union, profile first)
    # 4. Run transitive resolution
    # 5. Build extended GovernanceResolution
    ...

Test Commands

# Run all status tests
pytest tests/specify_cli/status/ -v

# Run all constitution tests
pytest tests/specify_cli/constitution/ -v

# Run integration tests
pytest tests/integration/ -v -k "structured_identity or profile_constitution"

# Type check
mypy src/specify_cli/identity.py src/specify_cli/status/ src/specify_cli/constitution/

# Lint
ruff check src/specify_cli/identity.py src/specify_cli/status/ src/specify_cli/constitution/

Key Patterns to Follow

1. Frozen dataclasses for all new value objects (ActorIdentity, ResolvedReferenceGraph) 2. to_dict() / from_dict() for serialisation (match StatusEvent pattern) 3. Cycle detection via visited set (match depth_first_order() pattern) 4. Optional fallback for DoctrineService (match constraint C-003) 5. _load_yaml_id_catalog() reuse for new artifact types in catalog