Skip to content

reach.artifact

Data structures representing evaluation outcomes, confusion matrices, and serialized run artifacts.

Assemble, validate, and serialize schema-versioned evaluation artifacts.

Abstention

Bases: BaseModel

Report overall, false, and out-of-scope abstention rates with bounds.

Source code in src/reach/artifact.py
class Abstention(BaseModel):
    """Report overall, false, and out-of-scope abstention rates with bounds."""

    model_config = ConfigDict(frozen=True)

    rate: float
    false_rate: float
    out_of_scope_detection: float | None = None
    scored: Annotated[int, Field(ge=0)]
    abstentions: Annotated[int, Field(ge=0)]
    in_scope: Annotated[int, Field(ge=0)]
    false_abstentions: Annotated[int, Field(ge=0)]
    out_of_scope: int = Field(default=0, ge=0)
    out_of_scope_detected: int = Field(default=0, ge=0)
    attempts: Annotated[int, Field(ge=1)] = 1

    @computed_field
    @property
    def interval(self) -> Interval | None:
        """Calculate the cluster-adjusted Wilson confidence interval for overall abstention rate."""
        return cluster_wilson_interval(self.abstentions, self.scored, attempts=self.attempts)

    @computed_field
    @property
    def false_interval(self) -> Interval | None:
        """Calculate the cluster-adjusted Wilson confidence interval for false abstention rate."""
        return cluster_wilson_interval(
            self.false_abstentions,
            self.in_scope,
            attempts=self.attempts,
        )

    @computed_field
    @property
    def out_of_scope_interval(self) -> Interval | None:
        """Calculate the cluster Wilson confidence interval for out-of-scope detection rate."""
        return cluster_wilson_interval(
            self.out_of_scope_detected,
            self.out_of_scope,
            attempts=self.attempts,
        )

false_interval property

false_interval: Interval | None

Calculate the cluster-adjusted Wilson confidence interval for false abstention rate.

interval property

interval: Interval | None

Calculate the cluster-adjusted Wilson confidence interval for overall abstention rate.

out_of_scope_interval property

out_of_scope_interval: Interval | None

Calculate the cluster Wilson confidence interval for out-of-scope detection rate.

Artifact

Bases: BaseModel

Represent complete evaluation results, summary scores, and provenance.

Source code in src/reach/artifact.py
class Artifact(BaseModel):
    """Represent complete evaluation results, summary scores, and provenance."""

    model_config = ConfigDict(frozen=True, extra="forbid")

    # 1. Schema & Metadata
    schema_version: str = SCHEMA_VERSION
    catalog_id: str
    catalog_mode: CatalogMode
    catalog_size: Annotated[int, Field(ge=0)]
    catalog_target: str | None = None
    provenance: RunProvenance
    digests: Annotated[
        Provenance,
        Field(
            description="Configuration fingerprint, corpus digest, and query set digest.",
        ),
    ]
    verified_digests: tuple[str, ...] = Field(
        default=(),
        description="Provenance digests corroborated by individual probe results.",
    )
    resolved_roots: tuple[ResolvedRoot, ...] = ()
    contested_skills: tuple[ContestedSkill, ...] = Field(
        default=(),
        description="Skills provided by multiple roots and resolved by precedence.",
    )

    # 2. Executive scores
    scores: RunScores
    spread: Spread

    # 3. Run accounting counters
    probes: int = Field(default=0, ge=0)
    errors: int = Field(default=0, ge=0)
    spend_usd: float = Field(default=0.0, ge=0.0)
    reused: int = Field(default=0, ge=0)

    # 4. Diagnostics
    confusion: tuple[ConfusionPair, ...] = ()

    # 5. Leaf collections at the bottom
    skills: tuple[SkillScore, ...] = Field(
        default=(),
        description="Per-skill evaluation scores in catalog order.",
    )
    queries: tuple[QueryRecord, ...] = ()

    @field_validator("schema_version")
    @classmethod
    def _readable_version(cls, value: str) -> str:
        """Refuse a document written to a contract this code does not implement."""
        if value.split(".", maxsplit=1)[0] != SCHEMA_VERSION.split(".", maxsplit=1)[0]:
            msg = (
                f"artifact schema_version {value!r} is not readable by "
                f"{SCHEMA_VERSION!r}: the major version differs"
            )
            raise ValueError(
                msg,
            )
        return value

    @model_validator(mode="after")
    def _every_digest_is_stated(self) -> Self:
        """Validate that all required provenance digests are populated."""
        missing = [
            field
            for field in ("config_fingerprint", "corpus_digest", "queries_digest")
            if not getattr(self.digests, field)
        ]
        if missing:
            msg = f"an artifact must state every digest; missing: {missing}"
            raise ValueError(msg)
        return self

    @property
    def unreached(self) -> tuple[SkillScore, ...]:
        """Return the skills that were asked for and never arrived at across any turn."""
        return tuple(
            s
            for s in self.skills
            if s.recall is not None
            and (s.trajectory_recall if s.trajectory_recall is not None else s.recall) == 0.0
        )

    @property
    def attractors(self) -> tuple[SkillScore, ...]:
        """Return skills selected more often than requested in ground truth."""
        taking = (s for s in self.skills if s.reached + s.absorbed > s.probes)
        return tuple(sorted(taking, key=lambda s: (-s.absorbed, s.skill)))

    @property
    def unclean(self) -> tuple[QueryRecord, ...]:
        """Return the queries that did not agree with ground truth on every attempt."""
        return tuple(q for q in self.queries if q.probes > 0 and not q.clean)

    @classmethod
    def assemble(
        cls,
        composition: Composition,
        results: Sequence[ProbeResult],
        *,
        roots: Sequence[Path] | None = None,
        contested: Sequence[ContestedSkill] = (),
        sample_queries: int = DEFAULT_SAMPLE_QUERIES,
        fit: CatalogFit | None = None,
        spend_usd: float = 0.0,
        reused: int = 0,
        difficulty: Mapping[str, int | LexicalRankLike] | None = None,
        digests: Provenance | None = None,
        cross_check: bool = True,
    ) -> Artifact:
        """Assemble an Artifact from a validated Composition and probe results.

        Derive query set, catalog, skills, and configuration directly from the
        Composition instance, reducing the orchestration seam from fifteen
        parameters to two essential inputs.
        """
        return _ArtifactAssembler(
            results=results,
            query_set=composition.query_set,
            catalog=composition.catalog,
            skills=composition.skills,
            config=composition.config,
            roots=roots,
            contested=contested,
            sample_queries=sample_queries,
            fit=fit,
            spend_usd=spend_usd,
            reused=reused,
            difficulty=difficulty,
            digests=digests,
            cross_check=cross_check,
        ).assemble()

attractors property

attractors: tuple[SkillScore, ...]

Return skills selected more often than requested in ground truth.

unclean property

unclean: tuple[QueryRecord, ...]

Return the queries that did not agree with ground truth on every attempt.

unreached property

unreached: tuple[SkillScore, ...]

Return the skills that were asked for and never arrived at across any turn.

assemble classmethod

assemble(
    composition: Composition,
    results: Sequence[ProbeResult],
    *,
    roots: Sequence[Path] | None = None,
    contested: Sequence[ContestedSkill] = (),
    sample_queries: int = DEFAULT_SAMPLE_QUERIES,
    fit: CatalogFit | None = None,
    spend_usd: float = 0.0,
    reused: int = 0,
    difficulty: Mapping[str, int | LexicalRankLike]
    | None = None,
    digests: Provenance | None = None,
    cross_check: bool = True,
) -> Artifact

Assemble an Artifact from a validated Composition and probe results.

Derive query set, catalog, skills, and configuration directly from the Composition instance, reducing the orchestration seam from fifteen parameters to two essential inputs.

Source code in src/reach/artifact.py
@classmethod
def assemble(
    cls,
    composition: Composition,
    results: Sequence[ProbeResult],
    *,
    roots: Sequence[Path] | None = None,
    contested: Sequence[ContestedSkill] = (),
    sample_queries: int = DEFAULT_SAMPLE_QUERIES,
    fit: CatalogFit | None = None,
    spend_usd: float = 0.0,
    reused: int = 0,
    difficulty: Mapping[str, int | LexicalRankLike] | None = None,
    digests: Provenance | None = None,
    cross_check: bool = True,
) -> Artifact:
    """Assemble an Artifact from a validated Composition and probe results.

    Derive query set, catalog, skills, and configuration directly from the
    Composition instance, reducing the orchestration seam from fifteen
    parameters to two essential inputs.
    """
    return _ArtifactAssembler(
        results=results,
        query_set=composition.query_set,
        catalog=composition.catalog,
        skills=composition.skills,
        config=composition.config,
        roots=roots,
        contested=contested,
        sample_queries=sample_queries,
        fit=fit,
        spend_usd=spend_usd,
        reused=reused,
        difficulty=difficulty,
        digests=digests,
        cross_check=cross_check,
    ).assemble()

ConfusionPair

Bases: BaseModel

Record misroute pairings, probe counts, and sample queries.

Source code in src/reach/artifact.py
class ConfusionPair(BaseModel):
    """Record misroute pairings, probe counts, and sample queries."""

    model_config = ConfigDict(frozen=True)

    expected: str
    invoked: str
    probes: Annotated[int, Field(ge=1)]
    collisions: int = Field(default=0, ge=0)
    queries: tuple[SampleQuery, ...] = ()

    @model_validator(mode="after")
    def _collisions_are_a_subset(self) -> Self:
        """Validate that collisions do not exceed total probe count."""
        if self.collisions > self.probes:
            msg = (
                f"{self.expected} -> {self.invoked}: {self.collisions} collisions "
                f"out of {self.probes} probes"
            )
            raise ValueError(
                msg,
            )
        return self

ContestedSkill

Bases: BaseModel

Record a skill name provided by multiple roots and the resolved path used.

Source code in src/reach/artifact.py
class ContestedSkill(BaseModel):
    """Record a skill name provided by multiple roots and the resolved path used."""

    model_config = ConfigDict(frozen=True)

    name: str
    kept: Path
    dropped: tuple[Path, ...]
    ranked: bool = True

Leak

Bases: BaseModel

Record whether and how a query explicitly references its target skill.

Source code in src/reach/leak.py
class Leak(BaseModel):
    """Record whether and how a query explicitly references its target skill."""

    model_config = ConfigDict(frozen=True, extra="forbid")

    names_target: bool = False
    distinctive_tokens: tuple[str, ...] = ()

    @property
    def leaked(self) -> bool:
        """Return True if query names target skill or has distinctive tokens."""
        return self.names_target or bool(self.distinctive_tokens)

    @property
    def routes(self) -> tuple[str, ...]:
        """Return diagnostic strings explaining why this probe was flagged as leaking."""
        routes: list[str] = []
        if self.names_target:
            routes.append("names target")
        if self.distinctive_tokens:
            routes.append(", ".join(self.distinctive_tokens))
        return tuple(routes)

leaked property

leaked: bool

Return True if query names target skill or has distinctive tokens.

routes property

routes: tuple[str, ...]

Return diagnostic strings explaining why this probe was flagged as leaking.

LexicalRankLike

Bases: Protocol

Protocol for rank objects exposing a 1-based integer position.

Source code in src/reach/artifact.py
@runtime_checkable
class LexicalRankLike(Protocol):
    """Protocol for rank objects exposing a 1-based integer position."""

    @property
    def position(self) -> int:
        """Return the 1-based rank position of the target skill."""
        ...

position property

position: int

Return the 1-based rank position of the target skill.

NotHeadline

Bases: BaseModel

Hold secondary evaluation metrics (macro precision, recall, F1).

Source code in src/reach/artifact.py
class NotHeadline(BaseModel):
    """Hold secondary evaluation metrics (macro precision, recall, F1)."""

    model_config = ConfigDict(frozen=True)

    macro_f1: float
    macro_precision: float = 0.0
    macro_recall: float = 0.0
    labels: tuple[str, ...] = ()

QueryRecord

Bases: BaseModel

Stage 3 telemetry: summarize probe outcomes, confidence interval, and leak status.

Source code in src/reach/artifact.py
class QueryRecord(BaseModel):
    """Stage 3 telemetry: summarize probe outcomes, confidence interval, and leak status."""

    model_config = ConfigDict(frozen=True)

    query_id: NonBlankStr
    text: NonBlankStr
    kind: QueryKind | None = None
    expected_skill: str
    probes: int = Field(default=0, ge=0)
    hits: int = Field(default=0, ge=0)
    selections: tuple[str, ...] = ()
    difficulty_rank: int | None = None
    leak: Leak | None = None

    @property
    def clean(self) -> bool:
        """Return True if all probe attempts matched ground truth."""
        return self.probes > 0 and self.hits == self.probes

    @computed_field
    @property
    def interval(self) -> Interval | None:
        """Calculate the Wilson score confidence interval for query hit rate."""
        return wilson_interval(self.hits, self.probes)

clean property

clean: bool

Return True if all probe attempts matched ground truth.

interval property

interval: Interval | None

Calculate the Wilson score confidence interval for query hit rate.

ResolvedRoot

Bases: BaseModel

Record a source tree root and the count of skills loaded from it.

Source code in src/reach/artifact.py
class ResolvedRoot(BaseModel):
    """Record a source tree root and the count of skills loaded from it."""

    model_config = ConfigDict(frozen=True)

    path: Path
    skills: Annotated[int, Field(ge=0)]

RunProvenance

Bases: BaseModel

Record runtime, model, attempt count, and experimental arm for a run.

Source code in src/reach/artifact.py
class RunProvenance(BaseModel):
    """Record runtime, model, attempt count, and experimental arm for a run."""

    model_config = ConfigDict(frozen=True)

    runtime: str
    model: str
    resolved_model: str = ""
    attempts: Annotated[int, Field(ge=1)]
    arm: str
    condition: str = ""
    catalog_fit: CatalogFit | None = None

RunScores

Bases: BaseModel

Hold primary run-level evaluation scores and confidence intervals.

Source code in src/reach/artifact.py
class RunScores(BaseModel):
    """Hold primary run-level evaluation scores and confidence intervals."""

    model_config = ConfigDict(frozen=True)

    consistency: float
    top1_accuracy: float
    abstention: Abstention
    not_headline: NotHeadline
    unanimous_queries: Annotated[int, Field(ge=0)]
    observed_queries: Annotated[int, Field(ge=0)]
    top1_hits: Annotated[int, Field(ge=0)]
    entrypoint_hits: int = Field(default=0, ge=0)
    entrypoint_accuracy: float = 0.0
    trajectory_hits: int = Field(default=0, ge=0)
    trajectory_reachability: float = 0.0
    step_efficiency: Annotated[float | None, Field(ge=0.0, le=1.0)] = None
    skill_f1: Annotated[float | None, Field(ge=0.0, le=1.0)] = None
    redundancy: float = 0.0
    scored: Annotated[int, Field(ge=0)]
    attempts: Annotated[int, Field(ge=1)] = 1

    @computed_field
    @property
    def consistency_interval(self) -> Interval | None:
        """Calculate the Wilson confidence interval for query consistency."""
        return wilson_interval(self.unanimous_queries, self.observed_queries)

    @computed_field
    @property
    def top1_interval(self) -> Interval | None:
        """Calculate the cluster-adjusted Wilson confidence interval for top-1 accuracy."""
        return cluster_wilson_interval(self.top1_hits, self.scored, attempts=self.attempts)

    @computed_field
    @property
    def entrypoint_interval(self) -> Interval | None:
        """Calculate the cluster-adjusted Wilson confidence interval for entrypoint accuracy."""
        return cluster_wilson_interval(self.entrypoint_hits, self.scored, attempts=self.attempts)

    @computed_field
    @property
    def trajectory_interval(self) -> Interval | None:
        """Calculate the cluster-adjusted Wilson confidence interval for trajectory reachability."""
        return cluster_wilson_interval(self.trajectory_hits, self.scored, attempts=self.attempts)

    @model_validator(mode="after")
    def _rates_match_the_counts_they_came_from(self) -> Self:
        """Validate that stored rates match underlying hit and total counts."""
        for name, rate, hits, total in (
            (
                "consistency",
                self.consistency,
                self.unanimous_queries,
                self.observed_queries,
            ),
            ("top1_accuracy", self.top1_accuracy, self.top1_hits, self.scored),
            (
                "entrypoint_accuracy",
                self.entrypoint_accuracy,
                self.entrypoint_hits,
                self.scored,
            ),
            (
                "trajectory_reachability",
                self.trajectory_reachability,
                self.trajectory_hits,
                self.scored,
            ),
        ):
            if not total:
                continue
            if abs(rate - hits / total) > RATE_TOLERANCE:
                msg = f"{name} is {rate} but its counts give {hits}/{total}"
                raise ValueError(msg)
        return self

consistency_interval property

consistency_interval: Interval | None

Calculate the Wilson confidence interval for query consistency.

entrypoint_interval property

entrypoint_interval: Interval | None

Calculate the cluster-adjusted Wilson confidence interval for entrypoint accuracy.

top1_interval property

top1_interval: Interval | None

Calculate the cluster-adjusted Wilson confidence interval for top-1 accuracy.

trajectory_interval property

trajectory_interval: Interval | None

Calculate the cluster-adjusted Wilson confidence interval for trajectory reachability.

SampleQuery

Bases: BaseModel

Quote one query verbatim with probe count and model reasoning traces.

Source code in src/reach/artifact.py
class SampleQuery(BaseModel):
    """Quote one query verbatim with probe count and model reasoning traces."""

    model_config = ConfigDict(frozen=True)

    query_id: str
    text: str
    probes: Annotated[
        int,
        Field(
            ge=1,
            description="Number of attempts that resulted in this confusion pairing.",
        ),
    ]
    reasoning: tuple[str, ...] = ()

SkillScore

Bases: BaseModel

Represent precision, recall, and uncertainty for an individual skill.

Source code in src/reach/artifact.py
class SkillScore(BaseModel):
    """Represent precision, recall, and uncertainty for an individual skill."""

    model_config = ConfigDict(frozen=True)

    skill: str
    root: Path | None = None
    attempts: Annotated[int, Field(ge=1)] = 1
    probes: Annotated[int, Field(ge=0)]
    reached: Annotated[int, Field(ge=0)]
    recall: float | None = None
    trajectory_reached: int = Field(default=0, ge=0)
    trajectory_recall: float | None = None
    absorbed: int = Field(default=0, ge=0)
    precision: float | None = None

    @model_validator(mode="after")
    def _rates_agree_with_their_counts(self) -> Self:
        """Tie each rate to the count it was taken over, in both directions."""
        if self.trajectory_reached < self.reached:
            msg = (
                f"{self.skill}: trajectory_reached ({self.trajectory_reached}) cannot "
                f"be less than reached ({self.reached})"
            )
            raise ValueError(msg)
        if self.reached > self.probes:
            msg = f"{self.skill}: reached ({self.reached}) cannot exceed probes ({self.probes})"
            raise ValueError(msg)
        if self.trajectory_reached > self.probes:
            msg = (
                f"{self.skill}: trajectory_reached ({self.trajectory_reached}) cannot "
                f"exceed probes ({self.probes})"
            )
            raise ValueError(msg)
        if (self.probes > 0) != (self.recall is not None):
            msg = (
                f"{self.skill}: recall is defined exactly when a query named the "
                f"skill; got recall={self.recall} over {self.probes} probes"
            )
            raise ValueError(msg)
        if (self.probes > 0) != (self.trajectory_recall is not None):
            msg = (
                f"{self.skill}: trajectory_recall is defined exactly when a query "
                f"named the skill; got trajectory_recall={self.trajectory_recall} "
                f"over {self.probes} probes"
            )
            raise ValueError(msg)
        if (self.reached + self.absorbed > 0) != (self.precision is not None):
            msg = (
                f"{self.skill}: precision is defined exactly when the skill was "
                f"selected; got precision={self.precision} over "
                f"{self.reached + self.absorbed} selections"
            )
            raise ValueError(msg)
        return self

    @computed_field
    @property
    def f1(self) -> float | None:
        """Return harmonic mean of precision and recall, or None if undefined."""
        if self.recall is None:
            return None
        return compute_f1(self.precision or 0.0, self.recall)

    @computed_field
    @property
    def recall_interval(self) -> Interval | None:
        """Calculate the cluster-adjusted Wilson confidence interval for this skill's recall."""
        return cluster_wilson_interval(self.reached, self.probes, attempts=self.attempts)

    @computed_field
    @property
    def precision_interval(self) -> Interval | None:
        """Calculate the cluster-adjusted Wilson confidence interval for this skill's precision."""
        return cluster_wilson_interval(
            self.reached,
            self.reached + self.absorbed,
            attempts=self.attempts,
        )

    @classmethod
    def from_class_metrics(
        cls,
        metrics: ClassMetrics,
        root: Path | None = None,
        attempts: int | None = None,
    ) -> SkillScore:
        """Construct a SkillScore model from ClassMetrics and optional root path."""
        selected = metrics.true_positives + metrics.false_positives
        traj_reached = max(metrics.trajectory_true_positives, metrics.true_positives)
        traj_recall = (traj_reached / metrics.support) if metrics.support else None
        return cls(
            skill=metrics.label,
            root=root,
            probes=metrics.support,
            reached=metrics.true_positives,
            recall=metrics.recall if metrics.support else None,
            trajectory_reached=traj_reached,
            trajectory_recall=traj_recall,
            absorbed=metrics.false_positives,
            precision=metrics.precision if selected else None,
            attempts=attempts if attempts is not None else metrics.attempts,
        )

f1 property

f1: float | None

Return harmonic mean of precision and recall, or None if undefined.

precision_interval property

precision_interval: Interval | None

Calculate the cluster-adjusted Wilson confidence interval for this skill's precision.

recall_interval property

recall_interval: Interval | None

Calculate the cluster-adjusted Wilson confidence interval for this skill's recall.

from_class_metrics classmethod

from_class_metrics(
    metrics: ClassMetrics,
    root: Path | None = None,
    attempts: int | None = None,
) -> SkillScore

Construct a SkillScore model from ClassMetrics and optional root path.

Source code in src/reach/artifact.py
@classmethod
def from_class_metrics(
    cls,
    metrics: ClassMetrics,
    root: Path | None = None,
    attempts: int | None = None,
) -> SkillScore:
    """Construct a SkillScore model from ClassMetrics and optional root path."""
    selected = metrics.true_positives + metrics.false_positives
    traj_reached = max(metrics.trajectory_true_positives, metrics.true_positives)
    traj_recall = (traj_reached / metrics.support) if metrics.support else None
    return cls(
        skill=metrics.label,
        root=root,
        probes=metrics.support,
        reached=metrics.true_positives,
        recall=metrics.recall if metrics.support else None,
        trajectory_reached=traj_reached,
        trajectory_recall=traj_recall,
        absorbed=metrics.false_positives,
        precision=metrics.precision if selected else None,
        attempts=attempts if attempts is not None else metrics.attempts,
    )

Spread

Bases: BaseModel

Summarize variance and standard error across probe replicates.

Source code in src/reach/artifact.py
class Spread(BaseModel):
    """Summarize variance and standard error across probe replicates."""

    model_config = ConfigDict(frozen=True)

    replicates: int = Field(default=0, ge=0)
    top1_by_attempt: tuple[float, ...] = ()
    mean: float | None = None
    repeated_queries: int = Field(default=0, ge=0)
    standard_error: Annotated[float, Field(ge=0.0)] | None = None
    skill_icc: Annotated[float, Field(ge=0.0, le=1.0)] | None = None

    @model_validator(mode="after")
    def _figures_match_what_was_observed(self) -> Self:
        """Validate replicate counts against observed statistics."""
        if len(self.top1_by_attempt) != self.replicates:
            msg = f"{self.replicates} replicates but {len(self.top1_by_attempt)} accuracies"
            raise ValueError(
                msg,
            )
        if (self.replicates > 0) != (self.mean is not None):
            msg = f"{self.replicates} replicates cannot mean to {self.mean}"
            raise ValueError(msg)
        if (self.replicates > 0) != (self.standard_error is not None):
            msg = (
                f"a probed run has an error on its estimate; got {self.replicates} "
                f"replicates and standard_error={self.standard_error}"
            )
            raise ValueError(
                msg,
            )
        return self

read_artifact

read_artifact(path: Path | str) -> Artifact

Read and validate an Artifact from a JSON file.

Source code in src/reach/artifact.py
def read_artifact(path: Path | str) -> Artifact:
    """Read and validate an Artifact from a JSON file."""
    return read_model(Artifact, path)

write_artifact

write_artifact(artifact: Artifact, path: Path) -> Path

Serialize and write a Artifact model to disk as formatted JSON.

Source code in src/reach/artifact.py
def write_artifact(artifact: Artifact, path: Path) -> Path:
    """Serialize and write a Artifact model to disk as formatted JSON."""
    destination = Path(path)
    if destination.exists() and not _is_artifact(destination):
        msg = (
            f"refusing to overwrite {destination}: it is not an artifact, and "
            "writing one here would destroy it. Artifacts belong at the path "
            "artifact_path() names."
        )
        raise ValueError(
            msg,
        )
    return write_model(artifact, destination)