153 lines
4.3 KiB
Python
153 lines
4.3 KiB
Python
|
|
"""
|
||
|
|
Resolution strategies for multi-space artifact lookup.
|
||
|
|
|
||
|
|
Implements FR-3.1: Deterministic resolution order
|
||
|
|
"""
|
||
|
|
|
||
|
|
from abc import ABC, abstractmethod
|
||
|
|
from typing import List, Optional
|
||
|
|
from dataclasses import dataclass, field
|
||
|
|
|
||
|
|
|
||
|
|
@dataclass
|
||
|
|
class ResolutionConfig:
|
||
|
|
"""
|
||
|
|
Configuration for resolution strategy.
|
||
|
|
|
||
|
|
Implements FR-3.1: Resolution order configuration
|
||
|
|
|
||
|
|
Resolution order:
|
||
|
|
1. Local InformationSpace
|
||
|
|
2. Explicitly included InformationSpaces
|
||
|
|
3. Default InformationSpace
|
||
|
|
4. Team/Shared InformationSpace (if configured)
|
||
|
|
|
||
|
|
Attributes:
|
||
|
|
space_id: Primary space ID
|
||
|
|
included_spaces: Explicitly included space IDs (ordered)
|
||
|
|
default_space_id: Default space for common artifacts
|
||
|
|
shared_space_id: Team/shared space (optional)
|
||
|
|
max_generation_depth: Maximum nesting depth for generators
|
||
|
|
"""
|
||
|
|
space_id: str
|
||
|
|
included_spaces: List[str] = field(default_factory=list)
|
||
|
|
default_space_id: Optional[str] = None
|
||
|
|
shared_space_id: Optional[str] = None
|
||
|
|
max_generation_depth: int = 3
|
||
|
|
|
||
|
|
def to_dict(self) -> dict:
|
||
|
|
"""Convert to dictionary for serialization."""
|
||
|
|
return {
|
||
|
|
"space_id": self.space_id,
|
||
|
|
"included_spaces": self.included_spaces,
|
||
|
|
"default_space_id": self.default_space_id,
|
||
|
|
"shared_space_id": self.shared_space_id,
|
||
|
|
"max_generation_depth": self.max_generation_depth,
|
||
|
|
}
|
||
|
|
|
||
|
|
@classmethod
|
||
|
|
def from_dict(cls, data: dict) -> "ResolutionConfig":
|
||
|
|
"""Create from dictionary."""
|
||
|
|
return cls(
|
||
|
|
space_id=data["space_id"],
|
||
|
|
included_spaces=data.get("included_spaces", []),
|
||
|
|
default_space_id=data.get("default_space_id"),
|
||
|
|
shared_space_id=data.get("shared_space_id"),
|
||
|
|
max_generation_depth=data.get("max_generation_depth", 3),
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class ResolutionStrategy(ABC):
|
||
|
|
"""
|
||
|
|
Abstract base class for resolution strategies.
|
||
|
|
|
||
|
|
Defines how to search for artifacts across multiple spaces.
|
||
|
|
"""
|
||
|
|
|
||
|
|
@abstractmethod
|
||
|
|
def get_search_order(self, config: ResolutionConfig) -> List[str]:
|
||
|
|
"""
|
||
|
|
Get ordered list of space IDs to search.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
config: Resolution configuration
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Ordered list of space IDs (no duplicates)
|
||
|
|
"""
|
||
|
|
pass
|
||
|
|
|
||
|
|
|
||
|
|
class MultiSpaceResolutionStrategy(ResolutionStrategy):
|
||
|
|
"""
|
||
|
|
Multi-space resolution strategy with deterministic search order.
|
||
|
|
|
||
|
|
Implements FR-3.1: Resolution order
|
||
|
|
1. Local space
|
||
|
|
2. Included spaces (in order)
|
||
|
|
3. Default space
|
||
|
|
4. Shared space
|
||
|
|
"""
|
||
|
|
|
||
|
|
def get_search_order(self, config: ResolutionConfig) -> List[str]:
|
||
|
|
"""
|
||
|
|
Get deterministic search order.
|
||
|
|
|
||
|
|
Implements FR-3.1 resolution order:
|
||
|
|
1. Local InformationSpace (config.space_id)
|
||
|
|
2. Explicitly included InformationSpaces (config.included_spaces)
|
||
|
|
3. Default InformationSpace (config.default_space_id)
|
||
|
|
4. Team/Shared InformationSpace (config.shared_space_id)
|
||
|
|
|
||
|
|
Removes duplicates while preserving order.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
config: Resolution configuration
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Ordered list of unique space IDs
|
||
|
|
"""
|
||
|
|
search_order = []
|
||
|
|
seen = set()
|
||
|
|
|
||
|
|
def add_if_not_seen(space_id: Optional[str]) -> None:
|
||
|
|
"""Add space ID if not None and not already seen."""
|
||
|
|
if space_id and space_id not in seen:
|
||
|
|
search_order.append(space_id)
|
||
|
|
seen.add(space_id)
|
||
|
|
|
||
|
|
# 1. Local space (highest priority)
|
||
|
|
add_if_not_seen(config.space_id)
|
||
|
|
|
||
|
|
# 2. Included spaces (in order)
|
||
|
|
for included_id in config.included_spaces:
|
||
|
|
add_if_not_seen(included_id)
|
||
|
|
|
||
|
|
# 3. Default space
|
||
|
|
add_if_not_seen(config.default_space_id)
|
||
|
|
|
||
|
|
# 4. Shared space (lowest priority)
|
||
|
|
add_if_not_seen(config.shared_space_id)
|
||
|
|
|
||
|
|
return search_order
|
||
|
|
|
||
|
|
|
||
|
|
class SingleSpaceResolutionStrategy(ResolutionStrategy):
|
||
|
|
"""
|
||
|
|
Simple strategy that only searches the local space.
|
||
|
|
|
||
|
|
Useful for isolated execution or testing.
|
||
|
|
"""
|
||
|
|
|
||
|
|
def get_search_order(self, config: ResolutionConfig) -> List[str]:
|
||
|
|
"""
|
||
|
|
Return only the local space.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
config: Resolution configuration
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
List containing only the local space ID
|
||
|
|
"""
|
||
|
|
return [config.space_id]
|