Files
markitect-main/markitect/prompts/repositories/interfaces.py
tegwick 945544880d feat(prompts): implement Phase 1 - Foundation (FR-1)
Implement addressable artifacts with content-based identity and change detection.

Core Features:
- Artifact model with SHA-256 content digests
- ArtifactReference for cross-space addressing
- IArtifactRepository interface for pluggable storage
- SQLiteArtifactRepository implementation
- ArtifactService for high-level operations
- Content digest calculation utilities

Database:
- prompt_artifacts table with indexes
- Support for artifact metadata and types
- UNIQUE constraint on space_id+name

Tests (41 passing):
- 26 model tests (metadata, artifacts, references, digests)
- 15 repository tests (CRUD, queries, constraints)

Implements:
- FR-1.1: Unique addressability by name and ID
- FR-1.2: Content digest computation and storage
- FR-1.3: Cross-space artifact references

Files Created:
- markitect/prompts/models.py
- markitect/prompts/repositories/interfaces.py
- markitect/prompts/repositories/sqlite.py
- markitect/prompts/services/artifact_service.py
- migrations/prompts/001_create_artifacts_table.sql
- tests/unit/prompts/test_artifact_models.py
- tests/unit/prompts/test_artifact_repository.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 22:30:26 +01:00

160 lines
3.6 KiB
Python

"""
Repository interfaces for artifact persistence.
Defines abstract interfaces for artifact storage, enabling
pluggable storage backends.
"""
from abc import ABC, abstractmethod
from typing import List, Optional
from markitect.prompts.models import Artifact, ArtifactType
class RepositoryError(Exception):
"""Base exception for repository errors."""
pass
class ArtifactNotFoundError(RepositoryError):
"""Raised when an artifact cannot be found."""
pass
class DuplicateArtifactError(RepositoryError):
"""Raised when attempting to create an artifact with duplicate name."""
pass
class IArtifactRepository(ABC):
"""
Abstract interface for artifact persistence.
Implements FR-1: InformationSpace Addressability
Provides CRUD operations for artifacts with content digest tracking.
"""
@abstractmethod
def create(self, artifact: Artifact) -> Artifact:
"""
Persist a new artifact.
Args:
artifact: Artifact to create
Returns:
Created artifact
Raises:
DuplicateArtifactError: If artifact with same space_id+name exists
RepositoryError: On other persistence errors
"""
pass
@abstractmethod
def get_by_id(self, artifact_id: str) -> Optional[Artifact]:
"""
Retrieve artifact by ID.
Args:
artifact_id: Artifact identifier
Returns:
Artifact if found, None otherwise
"""
pass
@abstractmethod
def get_by_name(self, space_id: str, name: str) -> Optional[Artifact]:
"""
Retrieve artifact by space and name.
Implements FR-1.3: Cross-space artifact lookup
Args:
space_id: Space identifier
name: Artifact name
Returns:
Artifact if found, None otherwise
"""
pass
@abstractmethod
def get_by_digest(self, content_digest: str) -> List[Artifact]:
"""
Find artifacts with matching content digest.
Implements FR-1.2: Content digest queries
Args:
content_digest: SHA-256 digest to match
Returns:
List of artifacts with matching digest
"""
pass
@abstractmethod
def list_by_space(
self,
space_id: str,
artifact_type: Optional[ArtifactType] = None,
) -> List[Artifact]:
"""
List all artifacts in a space.
Args:
space_id: Space identifier
artifact_type: Optional type filter
Returns:
List of artifacts in space
"""
pass
@abstractmethod
def update(self, artifact: Artifact) -> Artifact:
"""
Update an existing artifact.
Updates content digest and modified timestamp.
Args:
artifact: Artifact with updated data
Returns:
Updated artifact
Raises:
ArtifactNotFoundError: If artifact doesn't exist
RepositoryError: On other persistence errors
"""
pass
@abstractmethod
def delete(self, artifact_id: str) -> bool:
"""
Delete an artifact.
Args:
artifact_id: Artifact identifier
Returns:
True if deleted, False if not found
"""
pass
@abstractmethod
def exists(self, space_id: str, name: str) -> bool:
"""
Check if artifact exists.
Args:
space_id: Space identifier
name: Artifact name
Returns:
True if artifact exists
"""
pass