from typing import Any from pydantic import BaseModel, Field class DependencySpec(BaseModel): name: str min_version: str = "0.0.0" max_version: str = "9999.9999.9999" class MasteryProfileSpec(BaseModel): template: str | None = None required_dimensions: list[str] = Field(default_factory=list) dimension_threshold_overrides: dict[str, float] = Field(default_factory=dict) class CrossPackLinkSpec(BaseModel): source_concept: str target_concept: str relation: str class ProfileTemplateSpec(BaseModel): required_dimensions: list[str] = Field(default_factory=list) dimension_threshold_overrides: dict[str, float] = Field(default_factory=dict) class PackManifest(BaseModel): name: str display_name: str version: str schema_version: str didactopus_min_version: str didactopus_max_version: str description: str = "" author: str = "" license: str = "unspecified" dependencies: list[DependencySpec] = Field(default_factory=list) overrides: list[str] = Field(default_factory=list) profile_templates: dict[str, ProfileTemplateSpec] = Field(default_factory=dict) cross_pack_links: list[CrossPackLinkSpec] = Field(default_factory=list) class ConceptEntry(BaseModel): id: str title: str description: str = "" prerequisites: list[str] = Field(default_factory=list) mastery_signals: list[str] = Field(default_factory=list) mastery_profile: MasteryProfileSpec = Field(default_factory=MasteryProfileSpec) class ConceptsFile(BaseModel): concepts: list[ConceptEntry] class RoadmapStageEntry(BaseModel): id: str title: str concepts: list[str] = Field(default_factory=list) checkpoint: list[str] = Field(default_factory=list) class RoadmapFile(BaseModel): stages: list[RoadmapStageEntry] class ProjectEntry(BaseModel): id: str title: str difficulty: str = "" prerequisites: list[str] = Field(default_factory=list) deliverables: list[str] = Field(default_factory=list) class ProjectsFile(BaseModel): projects: list[ProjectEntry] class RubricsFile(BaseModel): rubrics: list[dict[str, Any]]