Files
markitect-main/markitect/prompts/resolver/strategy.py
tegwick 5f463e5b20 feat(prompts): implement Phase 3 - Resolver Engine (FR-3)
Implement deterministic multi-space resolution with configurable search order.

Core Features:
- ResolutionContext and ResolutionResult for tracking resolution state
- MultiSpaceResolutionStrategy implementing FR-3.1 search order:
  1. Local InformationSpace
  2. Explicitly included InformationSpaces
  3. Default InformationSpace
  4. Team/Shared InformationSpace
- PromptResolver with macro resolution logic
- ContextCompiler for assembling resolved prompts
- ResolutionConfig for configurable resolution behavior

Resolution Behavior:
- Required macros fail if not found (FR-3.2)
- Optional macros resolve to empty (FR-3.3)
- Generate macros detected for deferred execution (FR-3.4)
- Deterministic search order with duplicate removal
- Partial compilation support for debugging

Tests (31 passing):
- 14 strategy tests (search order, duplicates, priority)
- 9 resolver tests (required, optional, generate, multi-space)
- 8 compiler tests (substitution, dependencies, digests)

Implements:
- FR-3.1: Deterministic resolution order
- FR-3.2: Required macro validation
- FR-3.3: Optional macro fallback
- FR-3.4: Generate macro detection
- FR-3.5: Max generation depth configuration

Files Created:
- markitect/prompts/resolver/models.py
- markitect/prompts/resolver/strategy.py
- markitect/prompts/resolver/resolver.py
- markitect/prompts/resolver/compiler.py
- migrations/prompts/002_create_resolution_config.sql
- tests/unit/prompts/test_resolution_strategy.py
- tests/unit/prompts/test_prompt_resolver.py
- tests/unit/prompts/test_context_compiler.py

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

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]