diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index edd2afd4..1d470651 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: # Code formatting - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.5 + rev: v0.16.8 hooks: - id: ruff args: [--fix] @@ -30,7 +30,7 @@ repos: # Type checking - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.14.0 + rev: v2.3.1 hooks: - id: mypy args: [--ignore-missing-imports, --follow-imports=silent] diff --git a/CLAUDE.md b/CLAUDE.md index 3db15142..49899c39 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -27,11 +27,13 @@ status: active # ✅ GOOD — sanitized label, parameterized values, explicit types from engine.utils.security import sanitize_label + async def query_candidates(driver: GraphDriver, spec: DomainSpec) -> list[dict[str, Any]]: label = sanitize_label(spec.targetnode) cypher = f"MATCH (n:{label}) WHERE n.active = $active RETURN n LIMIT $limit" return await driver.execute_query(cypher, {"active": True, "limit": settings.max_results}) + # 🚫 BAD — unsanitized label, hardcoded limit, no type hints async def query_candidates(driver, spec): cypher = f"MATCH (n:{spec.targetnode}) WHERE n.active = true RETURN n LIMIT 25" @@ -47,6 +49,7 @@ def validate_weights(weights: dict[str, float] | None = None) -> None: msg = f"Weight sum {sum(weights.values()):.4f} exceeds 1.0 ceiling" raise ValidationError(msg) + # 🚫 BAD — f-string in raise, implicit Optional, no flag gate def validate_weights(weights: dict = None): if weights and sum(weights.values()) > 1.0: @@ -57,10 +60,12 @@ def validate_weights(weights: dict = None): # ✅ GOOD — gate type extends BaseGate, registered in enum class ProximityGate(BaseGate): """Gate that filters by graph distance.""" + def compile_where(self, spec: GateSpec, domain: DomainSpec) -> str: field = sanitize_label(spec.candidateprop) return f"candidate.{field} <= $max_distance" + # 🚫 BAD — standalone function, no BaseGate, no sanitization def proximity_gate(spec, domain): return f"candidate.{spec.candidateprop} <= {spec.threshold}" diff --git a/GUARDRAILS.md b/GUARDRAILS.md index 34682c6a..a1a8984f 100644 --- a/GUARDRAILS.md +++ b/GUARDRAILS.md @@ -68,6 +68,7 @@ The following are **absolutely prohibited** throughout the codebase: ```python # ✅ REQUIRED from cachetools import TTLCache + _cache: TTLCache = TTLCache(maxsize=1000, ttl=300) # 🚫 FORBIDDEN diff --git a/TESTING.md b/TESTING.md index b331a92e..2e997b96 100644 --- a/TESTING.md +++ b/TESTING.md @@ -56,9 +56,9 @@ def test_proximity_gate_compiles_sanitized_label(): domain = make_domain_spec(targetnode="Contact") gate = ProximityGate() clause = gate.compile_where(spec, domain) - assert "$max_distance" in clause # parameterized - assert "Contact" not in clause # label not in WHERE - assert "distance" in clause # prop name present + assert "$max_distance" in clause # parameterized + assert "Contact" not in clause # label not in WHERE + assert "distance" in clause # prop name present ``` ### Scoring Math Tests @@ -69,6 +69,7 @@ def test_lift_formula_clamps_to_bounds(): weight = calc.compute(positive_count=5, total_count=5, base_rate=0.5) assert 0.1 <= weight <= 2.0 + def test_confidence_dampening_reduces_uncertain_weights(): weight_small_sample = calc.compute(positive_count=2, total_count=3, base_rate=0.5) weight_large_sample = calc.compute(positive_count=200, total_count=300, base_rate=0.5) @@ -115,6 +116,7 @@ def test_age_gate_rejected_at_compile_time(): with pytest.raises(ProhibitedFactorError, match="age"): gate_compiler.compile(spec, domain) + def test_gender_scoring_dimension_blocked(): spec = ScoringDimension(field="gender") with pytest.raises(ProhibitedFactorError, match="gender"): @@ -147,11 +149,13 @@ Contract C-001 through C-024 must all pass before any merge. # tests/property/test_score_bounds.py from hypothesis import given, strategies as st + @given(st.floats(min_value=-1000, max_value=1000)) def test_score_always_clamped(raw_score): result = clamp_score(raw_score) assert 0.0 <= result <= 1.0 + @given(st.dictionaries(st.text(), st.floats(0, 1), min_size=1)) def test_weight_sum_assertion(weights): assume(sum(weights.values()) <= 1.0) diff --git a/agents/cursor/README.md b/agents/cursor/README.md index 5801e275..9fd959a8 100644 --- a/agents/cursor/README.md +++ b/agents/cursor/README.md @@ -167,7 +167,6 @@ class RetrievalSource: """Decision engine managing cursor context retrieval order, ensuring cache and memory checks precede repository scans for efficient knowledge access.""" # Key methods: - ``` **Lines:** 41-60 in `cursor_retrieval_kernel.py` @@ -203,7 +202,6 @@ class AutonomyLevel: """Graduated autonomy levels in GMP v2.0.""" # Key methods: - ``` **Lines:** 64-70 in `gmp_meta_learning.py` @@ -242,15 +240,19 @@ from pydantic import BaseModel from typing import Optional from datetime import datetime, timezone + class AgentsCursorRequest(BaseModel): """Request model for agents_cursor operations.""" + id: str data: dict timestamp: datetime correlation_id: Optional[str] = None + class AgentsCursorResponse(BaseModel): """Response model for agents_cursor operations.""" + success: bool result: Optional[dict] = None error: Optional[str] = None diff --git a/agents/cursor/cursor_session_hooks.py b/agents/cursor/cursor_session_hooks.py index d7175a25..599fac63 100644 --- a/agents/cursor/cursor_session_hooks.py +++ b/agents/cursor/cursor_session_hooks.py @@ -105,6 +105,7 @@ async def on_action( branch: str, tool_id: str, args: dict[str, Any], + *, success: bool = True, error: str | None = None, repo_state_hash: str | None = None, diff --git a/agents/cursor/docs/CURSOR-L9-INTEGRATION.md b/agents/cursor/docs/CURSOR-L9-INTEGRATION.md index d15ceb98..124d0d26 100644 --- a/agents/cursor/docs/CURSOR-L9-INTEGRATION.md +++ b/agents/cursor/docs/CURSOR-L9-INTEGRATION.md @@ -93,8 +93,8 @@ import httpx from pydantic import BaseModel # NOT L9 Pattern -import logging # Use structlog instead -import requests # Use httpx instead +import logging # Use structlog instead +import requests # Use httpx instead ``` --- @@ -215,12 +215,12 @@ When generating code for L9, Cursor MUST follow these patterns from the kernels: ```python # REQUIRED - Always use these -import structlog # NOT logging -import httpx # NOT requests +import structlog # NOT logging +import httpx # NOT requests from pydantic import BaseModel # Pydantic v2 (not v1) # FORBIDDEN - Never use these -import logging # Use structlog instead +import logging # Use structlog instead import requests # Use httpx instead ``` diff --git a/agents/cursor/docs/PRODUCTION-SPEED-PACK.md b/agents/cursor/docs/PRODUCTION-SPEED-PACK.md index 712d8731..3e13858c 100644 --- a/agents/cursor/docs/PRODUCTION-SPEED-PACK.md +++ b/agents/cursor/docs/PRODUCTION-SPEED-PACK.md @@ -54,21 +54,21 @@ import logging router = APIRouter(prefix="/api/v1", tags=["resource"]) logger = logging.getLogger(__name__) + class ResourceCreate(BaseModel): name: str description: Optional[str] = None + class ResourceResponse(BaseModel): id: int name: str description: Optional[str] created_at: str + @router.post("/resources", response_model=ResourceResponse, status_code=201) -async def create_resource( - resource: ResourceCreate, - db = Depends(get_db) -) -> ResourceResponse: +async def create_resource(resource: ResourceCreate, db=Depends(get_db)) -> ResourceResponse: """ Create a new resource. @@ -209,24 +209,28 @@ def process_order(order): # Save db.save(order) + # After def process_order(order): validate_order(order) order.total = calculate_order_total(order) save_order(order) + def validate_order(order): if not order.items: raise ValueError("Empty order") if order.total < 0: raise ValueError("Negative total") + def calculate_order_total(order): TAX_RATE = 0.08 subtotal = sum(item.price * item.quantity for item in order.items) tax = subtotal * TAX_RATE return subtotal + tax + def save_order(order): db.save(order) ``` @@ -247,27 +251,30 @@ def calculate_shipping(order_type, weight): elif order_type == "overnight": return weight * 3.0 + # After class ShippingStrategy: - def calculate(self, weight): pass + def calculate(self, weight): + pass + class StandardShipping(ShippingStrategy): def calculate(self, weight): return weight * 0.5 + class ExpressShipping(ShippingStrategy): def calculate(self, weight): return weight * 1.5 + class OvernightShipping(ShippingStrategy): def calculate(self, weight): return weight * 3.0 -SHIPPING_STRATEGIES = { - "standard": StandardShipping(), - "express": ExpressShipping(), - "overnight": OvernightShipping() -} + +SHIPPING_STRATEGIES = {"standard": StandardShipping(), "express": ExpressShipping(), "overnight": OvernightShipping()} + def calculate_shipping(order_type, weight): strategy = SHIPPING_STRATEGIES.get(order_type) @@ -289,10 +296,12 @@ def calculate_discount(total): return total * 0.1 return 0 + # After DISCOUNT_THRESHOLD = 100 DISCOUNT_RATE = 0.1 + def calculate_discount(total): if total > DISCOUNT_THRESHOLD: return total * DISCOUNT_RATE @@ -334,13 +343,16 @@ for order in orders: from functools import lru_cache import redis + # In-memory cache for pure functions @lru_cache(maxsize=1000) def expensive_calculation(n: int) -> int: return sum(i**2 for i in range(n)) + # Redis cache for API responses -redis_client = redis.Redis(host='localhost', port=6379) +redis_client = redis.Redis(host="localhost", port=6379) + async def get_user(user_id: int): cache_key = f"user:{user_id}" @@ -409,6 +421,7 @@ from functools import wraps logger = logging.getLogger(__name__) + def log_execution(func): @wraps(func) async def wrapper(*args, **kwargs): @@ -420,8 +433,10 @@ def log_execution(func): except Exception as e: logger.error(f"{func.__name__} failed: {e}", exc_info=True) raise + return wrapper + @log_execution async def process_payment(order_id: int, amount: float): # Implementation @@ -440,12 +455,8 @@ class PaymentError(Exception): super().__init__(self.message) def to_dict(self): - return { - "error": self.message, - "order_id": self.order_id, - "amount": self.amount, - "provider": self.provider - } + return {"error": self.message, "order_id": self.order_id, "amount": self.amount, "provider": self.provider} + try: process_payment(order_id, amount, provider) @@ -464,18 +475,21 @@ except PaymentError as e: # Bad: Race condition counter = 0 + async def increment(): global counter temp = counter await asyncio.sleep(0.001) counter = temp + 1 + # Good: Thread-safe import asyncio counter_lock = asyncio.Lock() counter = 0 + async def increment(): global counter async with counter_lock: @@ -493,6 +507,7 @@ class DataProcessor: def add_listener(self, listener): self.listeners.append(listener) + # Good: Cleanup class DataProcessor: def __init__(self): diff --git a/agents/cursor/perplexity_research_results/01-15-2026 - memory-substrate-stages/PHASE-0-TODO-STAGE-4-BELIEF-REVISION.md b/agents/cursor/perplexity_research_results/01-15-2026 - memory-substrate-stages/PHASE-0-TODO-STAGE-4-BELIEF-REVISION.md index c1ab2349..466d378e 100644 --- a/agents/cursor/perplexity_research_results/01-15-2026 - memory-substrate-stages/PHASE-0-TODO-STAGE-4-BELIEF-REVISION.md +++ b/agents/cursor/perplexity_research_results/01-15-2026 - memory-substrate-stages/PHASE-0-TODO-STAGE-4-BELIEF-REVISION.md @@ -35,8 +35,10 @@ ```python # Stage 4: Belief Revision Models (GMP-STAGE4) + class ContradictionType(str, Enum): """Types of contradictions between facts.""" + DIRECT = "direct" SEMANTIC = "semantic" TEMPORAL = "temporal" @@ -46,6 +48,7 @@ class ContradictionType(str, Enum): class ResolutionStrategy(str, Enum): """Strategies for resolving belief conflicts.""" + REPLACE = "replace" BRANCH = "branch" MERGE = "merge" @@ -55,6 +58,7 @@ class ResolutionStrategy(str, Enum): class ConflictingFactPair(BaseModel): """Pair of facts in conflict.""" + fact_a_id: UUID fact_b_id: UUID contradiction_type: ContradictionType @@ -65,6 +69,7 @@ class ConflictingFactPair(BaseModel): class ConflictExplanation(BaseModel): """Explanation of conflict with resolution recommendation.""" + explanation_id: UUID = Field(default_factory=uuid4) conflict_pair_id: UUID contradiction_type: ContradictionType @@ -77,6 +82,7 @@ class ConflictExplanation(BaseModel): class ResolutionRecord(BaseModel): """Audit record of resolution execution.""" + resolution_id: UUID = Field(default_factory=uuid4) explanation_id: UUID selected_strategy: ResolutionStrategy @@ -89,6 +95,7 @@ class ResolutionRecord(BaseModel): class BeliefResolutionAuditRow(BaseModel): """DTO for belief_resolution_audit table.""" + audit_id: UUID resolution_id: UUID timestamp: datetime @@ -235,6 +242,7 @@ END $$; ```python # ADD to SubstrateRepository class (after existing methods, ~line 400+) + async def get_conflicting_facts_for_subject( self, subject: str, @@ -244,6 +252,7 @@ async def get_conflicting_facts_for_subject( """Get facts that may conflict with a given subject.""" ... + async def insert_belief_resolution_audit( self, resolution_id: UUID, @@ -256,6 +265,7 @@ async def insert_belief_resolution_audit( """Insert belief resolution audit record.""" ... + async def update_fact_contradiction_count( self, fact_id: UUID, @@ -264,6 +274,7 @@ async def update_fact_contradiction_count( """Increment contradiction count for a fact.""" ... + async def get_resolution_audit_history( self, fact_id: Optional[UUID] = None, @@ -287,6 +298,7 @@ async def get_resolution_audit_history( self._explanation_engine: Optional[ExplanationEngine] = None self._conflict_resolver: Optional[ConflictResolver] = None + # ADD accessor methods (after get_retention_engine, ~line 940) def get_explanation_engine(self) -> ExplanationEngine: """Get explanation engine instance (lazy initialization).""" @@ -295,6 +307,7 @@ def get_explanation_engine(self) -> ExplanationEngine: logger.info("Initializing explanation_engine...") from memory.explanation_engine import ExplanationEngine + self._explanation_engine = ExplanationEngine( semantic_service=self._semantic_service, # llm_client injected at runtime @@ -302,6 +315,7 @@ def get_explanation_engine(self) -> ExplanationEngine: logger.info("explanation_engine loaded successfully") return self._explanation_engine + def get_conflict_resolver(self) -> ConflictResolver: """Get conflict resolver instance (lazy initialization).""" if self._conflict_resolver is not None: @@ -309,6 +323,7 @@ def get_conflict_resolver(self) -> ConflictResolver: logger.info("Initializing conflict_resolver...") from memory.conflict_resolver import ConflictResolver + explanation_engine = self.get_explanation_engine() self._conflict_resolver = ConflictResolver( repository=self._repository, @@ -317,6 +332,7 @@ def get_conflict_resolver(self) -> ConflictResolver: logger.info("conflict_resolver loaded successfully") return self._conflict_resolver + # ADD high-level API method async def resolve_belief_conflicts( self, @@ -361,6 +377,7 @@ BELIEF_RESOLUTION_LATENCY = Histogram( buckets=[0.1, 0.5, 1.0, 2.0, 5.0, 10.0], ) + def record_belief_resolution( strategy: str, contradiction_type: str, @@ -370,6 +387,7 @@ def record_belief_resolution( BELIEF_RESOLUTIONS.labels(strategy=strategy, contradiction_type=contradiction_type).inc() BELIEF_RESOLUTION_LATENCY.observe(latency_seconds) + def record_contradiction_detection(contradiction_type: str) -> None: """Record contradiction detection.""" CONTRADICTION_DETECTIONS.labels(contradiction_type=contradiction_type).inc() diff --git a/agents/cursor/perplexity_research_results/01-15-2026 - memory-substrate-stages/stage4_belief_revision_system.md b/agents/cursor/perplexity_research_results/01-15-2026 - memory-substrate-stages/stage4_belief_revision_system.md index 62aad8a2..751cfe73 100644 --- a/agents/cursor/perplexity_research_results/01-15-2026 - memory-substrate-stages/stage4_belief_revision_system.md +++ b/agents/cursor/perplexity_research_results/01-15-2026 - memory-substrate-stages/stage4_belief_revision_system.md @@ -30,6 +30,7 @@ Production-Grade Explanation-Based Belief Revision System for LLM Memory integra ```python class ConfidenceLevel(str, Enum): """Enumeration of confidence levels for assertions and explanations.""" + VERY_LOW = "very_low" LOW = "low" MEDIUM = "medium" @@ -39,6 +40,7 @@ class ConfidenceLevel(str, Enum): class ContradictionType(str, Enum): """Types of contradictions that can be detected between facts.""" + DIRECT = "direct" # Direct contradiction: A and NOT A SEMANTIC = "semantic" # Semantic contradiction: incompatible meanings TEMPORAL = "temporal" # Temporal contradiction: ordering conflicts @@ -48,6 +50,7 @@ class ContradictionType(str, Enum): class ResolutionStrategy(str, Enum): """Strategies for resolving belief conflicts.""" + REPLACE = "replace" # Replace old belief with new belief BRANCH = "branch" # Both beliefs true in different contexts MERGE = "merge" # Synthesize beliefs into unified representation @@ -60,6 +63,7 @@ class ResolutionStrategy(str, Enum): ```python class Fact(BaseModel): """Represents a single fact stored in the knowledge graph.""" + fact_id: UUID = Field(default_factory=uuid4) content: str = Field(..., min_length=1, max_length=4096) entity_type: str @@ -73,13 +77,12 @@ class Fact(BaseModel): def is_currently_valid(self, as_of: Optional[datetime] = None) -> bool: check_time = as_of or datetime.now(timezone.utc) - return self.valid_from <= check_time and ( - self.valid_to is None or self.valid_to > check_time - ) + return self.valid_from <= check_time and (self.valid_to is None or self.valid_to > check_time) class ConflictingFactPair(BaseModel): """Represents a pair of facts that conflict with each other.""" + fact_a_id: UUID fact_b_id: UUID contradiction_type: ContradictionType @@ -90,6 +93,7 @@ class ConflictingFactPair(BaseModel): class ConflictExplanation(BaseModel): """Structured explanation of why two facts conflict and how to resolve.""" + explanation_id: UUID = Field(default_factory=uuid4) conflict_pair_id: UUID contradiction_type: ContradictionType @@ -106,6 +110,7 @@ class ConflictExplanation(BaseModel): class ResolutionRecord(BaseModel): """Records the outcome of a belief conflict resolution.""" + resolution_id: UUID = Field(default_factory=uuid4) explanation_id: UUID selected_strategy: ResolutionStrategy diff --git a/agents/cursor/perplexity_research_results/01-15-2026 - memory-substrate-stages/stage6_multi_agent_consensus.md b/agents/cursor/perplexity_research_results/01-15-2026 - memory-substrate-stages/stage6_multi_agent_consensus.md index 5c6a0c45..72126959 100644 --- a/agents/cursor/perplexity_research_results/01-15-2026 - memory-substrate-stages/stage6_multi_agent_consensus.md +++ b/agents/cursor/perplexity_research_results/01-15-2026 - memory-substrate-stages/stage6_multi_agent_consensus.md @@ -32,12 +32,13 @@ Production-Grade Multi-Agent Belief-Calibrated Consensus System (BCCS) for LLM C @dataclass class CalibrationScore: """Quantifies an agent's calibration quality and reliability.""" + agent_id: str domain: str confidence_score: float # 0.0-1.0, how well confidence aligns with accuracy domain_expertise: float # 0.0-1.0, experience in this domain - recent_accuracy: float # 0.0-1.0, recent correctness rate - ece_metric: float # 0.0-1.0, Expected Calibration Error (lower = better) + recent_accuracy: float # 0.0-1.0, recent correctness rate + ece_metric: float # 0.0-1.0, Expected Calibration Error (lower = better) sample_count: int samples_in_domain: int last_updated: datetime @@ -55,6 +56,7 @@ class CalibrationScore: ```python class AgentProposal(BaseModel): """Represents a single agent's proposal in a consensus round.""" + agent_id: str round_number: int = Field(ge=1, le=10) proposed_value: str @@ -67,6 +69,7 @@ class AgentProposal(BaseModel): class ConsensusDecision(BaseModel): """Final consensus decision with full provenance.""" + operation_id: UUID problem_description: str final_decision: str @@ -131,9 +134,9 @@ Multi-round BCCS protocol: ### Protocol Parameters ```python -MAX_ROUNDS = 10 # Maximum iterations +MAX_ROUNDS = 10 # Maximum iterations WEIGHT_THRESHOLD = 0.70 # 70% weighted agreement required -STABILITY_ROUNDS = 2 # Agreement must persist across 2 rounds +STABILITY_ROUNDS = 2 # Agreement must persist across 2 rounds ``` ### Consensus Pipeline diff --git a/agents/cursor/perplexity_research_results/01-16-2026 - predictive-memory-warming/perplexity-deep-research-output.md b/agents/cursor/perplexity_research_results/01-16-2026 - predictive-memory-warming/perplexity-deep-research-output.md index 9a6b0ecc..12013806 100644 --- a/agents/cursor/perplexity_research_results/01-16-2026 - predictive-memory-warming/perplexity-deep-research-output.md +++ b/agents/cursor/perplexity_research_results/01-16-2026 - predictive-memory-warming/perplexity-deep-research-output.md @@ -96,9 +96,11 @@ from uuid import uuid4 import structlog from prometheus_client import Counter, Histogram + # Type definitions and enums class GapSeverity(str, Enum): """Enumeration of knowledge gap severity levels.""" + LOW = "low" MEDIUM = "medium" HIGH = "high" @@ -108,6 +110,7 @@ class GapSeverity(str, Enum): @dataclass class KnowledgeGap: """Represents a detected knowledge gap in the entity graph.""" + gap_id: str = field(default_factory=lambda: str(uuid4())) gap_type: str = "" # "entity", "relationship", "attribute" severity: GapSeverity = GapSeverity.LOW @@ -117,17 +120,13 @@ class KnowledgeGap: # Prometheus metrics -gap_detection_count = Counter( - 'gap_detection_count', - 'Total gaps detected by gap detector', - ['gap_type', 'severity'] -) +gap_detection_count = Counter("gap_detection_count", "Total gaps detected by gap detector", ["gap_type", "severity"]) gap_detector_latency = Histogram( - 'gap_detector_latency_seconds', - 'Latency of gap detection operations', - ['operation'], - buckets=(0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0) + "gap_detector_latency_seconds", + "Latency of gap detection operations", + ["operation"], + buckets=(0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0), ) @@ -163,9 +162,7 @@ class GapDetector: self.critical_path_entities: Set[str] = set() async def detect_entity_gaps( - self, - mentioned_entities: List[str], - entity_graph: Dict[str, Set[str]] + self, mentioned_entities: List[str], entity_graph: Dict[str, Set[str]] ) -> List[KnowledgeGap]: """ Detect entities referenced but missing from the knowledge graph. @@ -188,10 +185,7 @@ class GapDetector: gaps: List[KnowledgeGap] = [] try: - await self.logger.ainfo( - "detecting_entity_gaps", - entity_count=len(mentioned_entities) - ) + await self.logger.ainfo("detecting_entity_gaps", entity_count=len(mentioned_entities)) if not mentioned_entities: raise ValueError("mentioned_entities cannot be empty") @@ -214,24 +208,15 @@ class GapDetector: severity = GapSeverity.CRITICAL if is_critical else GapSeverity.HIGH gap = KnowledgeGap( - gap_type="entity", - severity=severity, - entity_ids=[entity_id], - confidence_score=confidence + gap_type="entity", severity=severity, entity_ids=[entity_id], confidence_score=confidence ) gaps.append(gap) # Record metric - gap_detection_count.labels( - gap_type="entity", - severity=severity.value - ).inc() + gap_detection_count.labels(gap_type="entity", severity=severity.value).inc() await self.logger.ainfo( - "entity_gap_detected", - entity_id=entity_id, - confidence=confidence, - severity=severity.value + "entity_gap_detected", entity_id=entity_id, confidence=confidence, severity=severity.value ) elapsed = time.time() - start_time @@ -240,17 +225,11 @@ class GapDetector: return gaps except Exception as e: - await self.logger.aerror( - "entity_gap_detection_failed", - error=str(e), - entity_count=len(mentioned_entities) - ) + await self.logger.aerror("entity_gap_detection_failed", error=str(e), entity_count=len(mentioned_entities)) raise async def detect_relationship_gaps( - self, - mentioned_entities: List[str], - entity_graph: Dict[str, Set[str]] + self, mentioned_entities: List[str], entity_graph: Dict[str, Set[str]] ) -> List[KnowledgeGap]: """ Detect missing relationships between entities in the knowledge graph. @@ -277,10 +256,7 @@ class GapDetector: gaps: List[KnowledgeGap] = [] try: - await self.logger.ainfo( - "detecting_relationship_gaps", - entity_count=len(mentioned_entities) - ) + await self.logger.ainfo("detecting_relationship_gaps", entity_count=len(mentioned_entities)) if not mentioned_entities or len(mentioned_entities) < 2: return [] @@ -313,20 +289,14 @@ class GapDetector: gap_type="relationship", severity=GapSeverity.MEDIUM, entity_ids=[entity_a, entity_b], - confidence_score=confidence + confidence_score=confidence, ) gaps.append(gap) - gap_detection_count.labels( - gap_type="relationship", - severity=GapSeverity.MEDIUM.value - ).inc() + gap_detection_count.labels(gap_type="relationship", severity=GapSeverity.MEDIUM.value).inc() await self.logger.ainfo( - "relationship_gap_detected", - entity_a=entity_a, - entity_b=entity_b, - confidence=confidence + "relationship_gap_detected", entity_a=entity_a, entity_b=entity_b, confidence=confidence ) elapsed = time.time() - start_time @@ -335,16 +305,11 @@ class GapDetector: return gaps except Exception as e: - await self.logger.aerror( - "relationship_gap_detection_failed", - error=str(e) - ) + await self.logger.aerror("relationship_gap_detection_failed", error=str(e)) raise async def detect_all_gaps( - self, - mentioned_entities: List[str], - entity_graph: Dict[str, Set[str]] + self, mentioned_entities: List[str], entity_graph: Dict[str, Set[str]] ) -> List[KnowledgeGap]: """ Detect all gap types (entity, relationship, attribute). @@ -366,31 +331,21 @@ class GapDetector: start_time = time.time() try: - await self.logger.ainfo( - "detecting_all_gaps", - entity_count=len(mentioned_entities) - ) + await self.logger.ainfo("detecting_all_gaps", entity_count=len(mentioned_entities)) # Run detection operations concurrently entity_gaps, relationship_gaps = await asyncio.gather( self.detect_entity_gaps(mentioned_entities, entity_graph), self.detect_relationship_gaps(mentioned_entities, entity_graph), - return_exceptions=False + return_exceptions=False, ) # Combine results and sort by severity and confidence all_gaps = entity_gaps + relationship_gaps # Sort by severity (CRITICAL first) then by confidence (highest first) - severity_order = { - GapSeverity.CRITICAL: 0, - GapSeverity.HIGH: 1, - GapSeverity.MEDIUM: 2, - GapSeverity.LOW: 3 - } - all_gaps.sort( - key=lambda g: (severity_order[g.severity], -g.confidence_score) - ) + severity_order = {GapSeverity.CRITICAL: 0, GapSeverity.HIGH: 1, GapSeverity.MEDIUM: 2, GapSeverity.LOW: 3} + all_gaps.sort(key=lambda g: (severity_order[g.severity], -g.confidence_score)) elapsed = time.time() - start_time gap_detector_latency.labels(operation="detect_all_gaps").observe(elapsed) @@ -400,17 +355,13 @@ class GapDetector: total_gaps=len(all_gaps), entity_gaps=len(entity_gaps), relationship_gaps=len(relationship_gaps), - latency_ms=elapsed * 1000 + latency_ms=elapsed * 1000, ) return all_gaps except Exception as e: - await self.logger.aerror( - "all_gap_detection_failed", - error=str(e), - entity_count=len(mentioned_entities) - ) + await self.logger.aerror("all_gap_detection_failed", error=str(e), entity_count=len(mentioned_entities)) raise def update_gap_frequency(self, entity_id: str, increment: float = 1.0) -> None: @@ -441,10 +392,7 @@ class GapDetector: entity_ids: Set of entity IDs considered critical """ self.critical_path_entities = entity_ids - self.logger.info( - "critical_path_entities_updated", - count=len(entity_ids) - ) + self.logger.info("critical_path_entities_updated", count=len(entity_ids)) ``` ## Implementation: Predictive Cache Module @@ -482,6 +430,7 @@ except ImportError: @dataclass class SubgraphEntry: """Represents a cached subgraph entry for a knowledge graph entity.""" + entity_id: str neighbors: Dict[str, List[str]] = field(default_factory=dict) # rel_type -> [neighbor_ids] relationship_types: Dict[str, int] = field(default_factory=dict) # rel_type -> count @@ -503,6 +452,7 @@ class SubgraphEntry: @dataclass class CacheMetrics: """Metrics tracking cache performance.""" + cache_hits: int = 0 cache_misses: int = 0 total_warming_calls: int = 0 @@ -524,7 +474,7 @@ class PredictiveCacheConfig: cache_ttl_seconds: int = 300, max_subgraph_neighbors: int = 20, max_cache_entries: int = 1000, - warming_concurrency: int = 20 + warming_concurrency: int = 20, ): """ Initialize cache configuration. @@ -544,35 +494,23 @@ class PredictiveCacheConfig: # Prometheus metrics -cache_hits = Counter( - 'cache_hits_total', - 'Total cache hits', - ['cache_layer'] -) +cache_hits = Counter("cache_hits_total", "Total cache hits", ["cache_layer"]) -cache_misses = Counter( - 'cache_misses_total', - 'Total cache misses', - ['cache_layer'] -) +cache_misses = Counter("cache_misses_total", "Total cache misses", ["cache_layer"]) warming_latency = Histogram( - 'warming_latency_seconds', - 'Entity warming operation latency', - ['operation'], - buckets=(0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0) + "warming_latency_seconds", + "Entity warming operation latency", + ["operation"], + buckets=(0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0), ) -cache_entries = Gauge( - 'cache_entries_total', - 'Current number of entries in cache', - ['cache_layer'] -) +cache_entries = Gauge("cache_entries_total", "Current number of entries in cache", ["cache_layer"]) warming_operations = Counter( - 'warming_operations_total', - 'Total warming operations attempted', - ['status'] # success, failure + "warming_operations_total", + "Total warming operations attempted", + ["status"], # success, failure ) @@ -621,33 +559,25 @@ class PredictiveCache: try: if redis_async is None: await self.logger.awarning( - "redis_not_available", - message="redis.asyncio not installed, using L1 cache only" + "redis_not_available", message="redis.asyncio not installed, using L1 cache only" ) self._initialized = True return self.redis_client = await redis_async.from_url( - self.config.redis_url, - encoding="utf-8", - decode_responses=True + self.config.redis_url, encoding="utf-8", decode_responses=True ) # Test connectivity await self.redis_client.ping() await self.logger.ainfo( - "cache_initialized", - redis_url=self.config.redis_url, - cache_ttl_seconds=self.config.cache_ttl_seconds + "cache_initialized", redis_url=self.config.redis_url, cache_ttl_seconds=self.config.cache_ttl_seconds ) self._initialized = True except Exception as e: - await self.logger.aerror( - "cache_initialization_failed", - error=str(e) - ) + await self.logger.aerror("cache_initialization_failed", error=str(e)) raise async def warm_entity(self, entity_id: str) -> Optional[SubgraphEntry]: @@ -671,10 +601,7 @@ class PredictiveCache: try: async with self._warming_semaphore: - await self.logger.ainfo( - "warming_entity", - entity_id=entity_id - ) + await self.logger.ainfo("warming_entity", entity_id=entity_id) # Simulate Neo4j query for subgraph data # In production, replace with actual Neo4j driver call @@ -692,43 +619,30 @@ class PredictiveCache: if self.redis_client is not None: try: await self.redis_client.setex( - f"entity:{entity_id}", - self.config.cache_ttl_seconds, - subgraph_entry.to_json() + f"entity:{entity_id}", self.config.cache_ttl_seconds, subgraph_entry.to_json() ) except Exception as e: - await self.logger.awarning( - "redis_store_failed", - entity_id=entity_id, - error=str(e) - ) + await self.logger.awarning("redis_store_failed", entity_id=entity_id, error=str(e)) elapsed = time.time() - start_time warming_latency.labels(operation="warm_entity").observe(elapsed) warming_operations.labels(status="success").inc() self.metrics.total_warming_calls += 1 - self.metrics.avg_warming_latency_ms = ( - 0.9 * self.metrics.avg_warming_latency_ms + - 0.1 * (elapsed * 1000) - ) + self.metrics.avg_warming_latency_ms = 0.9 * self.metrics.avg_warming_latency_ms + 0.1 * (elapsed * 1000) await self.logger.ainfo( "entity_warmed", entity_id=entity_id, neighbors_count=sum(len(v) for v in subgraph_entry.neighbors.values()), - latency_ms=elapsed * 1000 + latency_ms=elapsed * 1000, ) return subgraph_entry except Exception as e: warming_operations.labels(status="failure").inc() - await self.logger.aerror( - "entity_warming_failed", - entity_id=entity_id, - error=str(e) - ) + await self.logger.aerror("entity_warming_failed", entity_id=entity_id, error=str(e)) return None async def warm_entities(self, entity_ids: List[str]) -> List[SubgraphEntry]: @@ -747,10 +661,7 @@ class PredictiveCache: start_time = time.time() try: - await self.logger.ainfo( - "warming_entities_batch", - entity_count=len(entity_ids) - ) + await self.logger.ainfo("warming_entities_batch", entity_count=len(entity_ids)) # Create warming tasks with gather tasks = [self.warm_entity(eid) for eid in entity_ids] @@ -766,17 +677,13 @@ class PredictiveCache: "entities_warming_complete", requested=len(entity_ids), successful=len(successful_entries), - latency_ms=elapsed * 1000 + latency_ms=elapsed * 1000, ) return successful_entries except Exception as e: - await self.logger.aerror( - "entities_warming_failed", - entity_count=len(entity_ids), - error=str(e) - ) + await self.logger.aerror("entities_warming_failed", entity_count=len(entity_ids), error=str(e)) return [] async def get_cached(self, entity_id: str) -> Optional[SubgraphEntry]: @@ -802,10 +709,7 @@ class PredictiveCache: cache_hits.labels(cache_layer="l1").inc() self.metrics.cache_hits += 1 - await self.logger.ainfo( - "cache_hit_l1", - entity_id=entity_id - ) + await self.logger.ainfo("cache_hit_l1", entity_id=entity_id) return entry @@ -819,10 +723,7 @@ class PredictiveCache: entry.accessed_count += 1 # Refresh TTL - await self.redis_client.expire( - f"entity:{entity_id}", - self.config.cache_ttl_seconds - ) + await self.redis_client.expire(f"entity:{entity_id}", self.config.cache_ttl_seconds) # Promote to L1 self.l1_cache[entity_id] = entry @@ -830,37 +731,23 @@ class PredictiveCache: cache_hits.labels(cache_layer="l2").inc() self.metrics.cache_hits += 1 - await self.logger.ainfo( - "cache_hit_l2", - entity_id=entity_id - ) + await self.logger.ainfo("cache_hit_l2", entity_id=entity_id) return entry except Exception as e: - await self.logger.awarning( - "redis_get_failed", - entity_id=entity_id, - error=str(e) - ) + await self.logger.awarning("redis_get_failed", entity_id=entity_id, error=str(e)) # Cache miss cache_misses.labels(cache_layer="both").inc() self.metrics.cache_misses += 1 - await self.logger.ainfo( - "cache_miss", - entity_id=entity_id - ) + await self.logger.ainfo("cache_miss", entity_id=entity_id) return None except Exception as e: - await self.logger.aerror( - "cache_get_failed", - entity_id=entity_id, - error=str(e) - ) + await self.logger.aerror("cache_get_failed", entity_id=entity_id, error=str(e)) return None def get_metrics(self) -> CacheMetrics: @@ -893,20 +780,12 @@ class PredictiveCache: neighbors = { "knows": ["entity_2", "entity_3", "entity_4"], "similar_to": ["entity_5", "entity_6"], - "related_to": ["entity_7"] + "related_to": ["entity_7"], } - relationship_types = { - "knows": 3, - "similar_to": 2, - "related_to": 1 - } + relationship_types = {"knows": 3, "similar_to": 2, "related_to": 1} - return SubgraphEntry( - entity_id=entity_id, - neighbors=neighbors, - relationship_types=relationship_types - ) + return SubgraphEntry(entity_id=entity_id, neighbors=neighbors, relationship_types=relationship_types) async def clear_expired(self) -> None: """ @@ -929,10 +808,7 @@ class PredictiveCache: cache_entries.labels(cache_layer="l1").set(len(self.l1_cache)) if expired_keys: - await self.logger.ainfo( - "expired_entries_cleared", - count=len(expired_keys) - ) + await self.logger.ainfo("expired_entries_cleared", count=len(expired_keys)) async def shutdown(self) -> None: """ @@ -944,10 +820,7 @@ class PredictiveCache: try: await self.redis_client.close() except Exception as e: - await self.logger.awarning( - "redis_close_failed", - error=str(e) - ) + await self.logger.awarning("redis_close_failed", error=str(e)) self.l1_cache.clear() await self.logger.ainfo("cache_shutdown_complete") @@ -985,28 +858,18 @@ from prometheus_client import Counter, Gauge, Histogram # Prometheus metrics -warming_service_calls = Counter( - 'warming_service_calls_total', - 'Total warming service calls', - ['operation', 'status'] -) +warming_service_calls = Counter("warming_service_calls_total", "Total warming service calls", ["operation", "status"]) warming_service_latency = Histogram( - 'warming_service_latency_seconds', - 'Warming service operation latency', - ['operation'], - buckets=(0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0) + "warming_service_latency_seconds", + "Warming service operation latency", + ["operation"], + buckets=(0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0), ) -entities_warmed_total = Counter( - 'entities_warmed_total', - 'Total entities warmed into cache' -) +entities_warmed_total = Counter("entities_warmed_total", "Total entities warmed into cache") -gaps_addressed = Counter( - 'gaps_addressed_total', - 'Total knowledge gaps addressed by warming' -) +gaps_addressed = Counter("gaps_addressed_total", "Total knowledge gaps addressed by warming") class MemoryWarmingService: @@ -1033,7 +896,7 @@ class MemoryWarmingService: def __init__( self, gap_detector: Any, # GapDetector instance - cache: Any # PredictiveCache instance + cache: Any, # PredictiveCache instance ): """ Initialize the memory warming service. @@ -1058,17 +921,11 @@ class MemoryWarmingService: await self.cache.initialize() await self.logger.ainfo("warming_service_initialized") except Exception as e: - await self.logger.aerror( - "warming_service_initialization_failed", - error=str(e) - ) + await self.logger.aerror("warming_service_initialization_failed", error=str(e)) raise async def warm_for_query( - self, - query: str, - mentioned_entities: List[str], - max_gaps_to_warm: int = 10 + self, query: str, mentioned_entities: List[str], max_gaps_to_warm: int = 10 ) -> Dict[str, Any]: """ Warm memory for an incoming query. @@ -1099,22 +956,17 @@ class MemoryWarmingService: try: await self.logger.ainfo( - "warming_for_query", - query_preview=query[:100], - entity_count=len(mentioned_entities) + "warming_for_query", query_preview=query[:100], entity_count=len(mentioned_entities) ) # Phase 1: Detect gaps - detected_gaps = await self.gap_detector.detect_all_gaps( - mentioned_entities, - self.entity_graph - ) + detected_gaps = await self.gap_detector.detect_all_gaps(mentioned_entities, self.entity_graph) await self.logger.ainfo( "gaps_detected", total_gaps=len(detected_gaps), critical=sum(1 for g in detected_gaps if g.severity.value == "critical"), - high=sum(1 for g in detected_gaps if g.severity.value == "high") + high=sum(1 for g in detected_gaps if g.severity.value == "high"), ) # Phase 2: Prioritize and select gaps to warm @@ -1146,17 +998,14 @@ class MemoryWarmingService: elapsed = time.time() - start_time warming_service_latency.labels(operation="warm_for_query").observe(elapsed) - warming_service_calls.labels( - operation="warm_for_query", - status="success" - ).inc() + warming_service_calls.labels(operation="warm_for_query", status="success").inc() result = { "gaps_detected": len(detected_gaps), "gaps_addressed": gaps_to_address, "entities_warmed": len(warmed_entities), "warming_latency_ms": elapsed * 1000, - "cache_metrics": self._format_cache_metrics() + "cache_metrics": self._format_cache_metrics(), } await self.logger.ainfo( @@ -1164,29 +1013,22 @@ class MemoryWarmingService: gaps_detected=len(detected_gaps), gaps_addressed=gaps_to_address, entities_warmed=len(warmed_entities), - latency_ms=elapsed * 1000 + latency_ms=elapsed * 1000, ) return result except Exception as e: - warming_service_calls.labels( - operation="warm_for_query", - status="failure" - ).inc() - - await self.logger.aerror( - "query_warming_failed", - error=str(e), - entity_count=len(mentioned_entities) - ) + warming_service_calls.labels(operation="warm_for_query", status="failure").inc() + + await self.logger.aerror("query_warming_failed", error=str(e), entity_count=len(mentioned_entities)) return { "gaps_detected": 0, "gaps_addressed": gaps_to_address, "entities_warmed": 0, "warming_latency_ms": (time.time() - start_time) * 1000, - "error": str(e) + "error": str(e), } def set_entity_graph(self, entity_graph: Dict[str, Set[str]]) -> None: @@ -1206,9 +1048,7 @@ class MemoryWarmingService: self.logger.info( "entity_graph_updated", entity_count=len(entity_graph), - total_relationships=sum( - len(neighbors) for neighbors in entity_graph.values() - ) + total_relationships=sum(len(neighbors) for neighbors in entity_graph.values()), ) def get_service_metrics(self) -> Dict[str, Any]: @@ -1232,11 +1072,11 @@ class MemoryWarmingService: "cache_misses": cache_metrics.cache_misses, "cache_hit_ratio": cache_metrics.cache_hit_ratio, "avg_warming_latency_ms": cache_metrics.avg_warming_latency_ms, - "total_warming_calls": cache_metrics.total_warming_calls + "total_warming_calls": cache_metrics.total_warming_calls, }, "warming_history": self._warming_history.copy(), "entity_graph_size": len(self.entity_graph), - "l1_cache_size": len(self.cache.l1_cache) + "l1_cache_size": len(self.cache.l1_cache), } def _format_cache_metrics(self) -> Dict[str, Any]: @@ -1254,7 +1094,7 @@ class MemoryWarmingService: "hits": metrics.cache_hits, "misses": metrics.cache_misses, "hit_ratio_percent": metrics.cache_hit_ratio, - "avg_latency_ms": metrics.avg_warming_latency_ms + "avg_latency_ms": metrics.avg_warming_latency_ms, } async def maintenance_cycle(self) -> None: @@ -1268,16 +1108,10 @@ class MemoryWarmingService: try: await self.cache.clear_expired() - await self.logger.ainfo( - "maintenance_cycle_complete", - l1_cache_size=len(self.cache.l1_cache) - ) + await self.logger.ainfo("maintenance_cycle_complete", l1_cache_size=len(self.cache.l1_cache)) except Exception as e: - await self.logger.aerror( - "maintenance_cycle_failed", - error=str(e) - ) + await self.logger.aerror("maintenance_cycle_failed", error=str(e)) async def shutdown(self) -> None: """ @@ -1290,10 +1124,7 @@ class MemoryWarmingService: await self.cache.shutdown() await self.logger.ainfo("warming_service_shutdown_complete") except Exception as e: - await self.logger.aerror( - "warming_service_shutdown_failed", - error=str(e) - ) + await self.logger.aerror("warming_service_shutdown_failed", error=str(e)) # Example usage and integration patterns @@ -1312,9 +1143,7 @@ async def example_warming_workflow(): # Initialize components gap_detector = GapDetector() cache_config = PredictiveCacheConfig( - redis_url="redis://localhost:6379", - cache_ttl_seconds=300, - warming_concurrency=20 + redis_url="redis://localhost:6379", cache_ttl_seconds=300, warming_concurrency=20 ) cache = PredictiveCache(cache_config) @@ -1329,14 +1158,13 @@ async def example_warming_workflow(): "entity_3": {"entity_1", "entity_6"}, "entity_4": {"entity_1"}, "entity_5": {"entity_2"}, - "entity_6": {"entity_3"} + "entity_6": {"entity_3"}, } service.set_entity_graph(entity_graph) # Warm for incoming query result = await service.warm_for_query( - query="Find all entities related to entity_1", - mentioned_entities=["entity_1", "entity_2", "entity_3"] + query="Find all entities related to entity_1", mentioned_entities=["entity_1", "entity_2", "entity_3"] ) print("Warming result:", result) diff --git a/agents/cursor/perplexity_research_results/01-16-2026 - predictive-memory-warming/stage5_predictive_memory_warming.md b/agents/cursor/perplexity_research_results/01-16-2026 - predictive-memory-warming/stage5_predictive_memory_warming.md index 169883d3..43164a38 100644 --- a/agents/cursor/perplexity_research_results/01-16-2026 - predictive-memory-warming/stage5_predictive_memory_warming.md +++ b/agents/cursor/perplexity_research_results/01-16-2026 - predictive-memory-warming/stage5_predictive_memory_warming.md @@ -31,6 +31,7 @@ Production-Grade Predictive Memory Warming System for LLM Agents achieving ~40% ```python class GapSeverity(str, Enum): """Enumeration of knowledge gap severity levels.""" + LOW = "low" MEDIUM = "medium" HIGH = "high" @@ -40,6 +41,7 @@ class GapSeverity(str, Enum): @dataclass class KnowledgeGap: """Represents a detected knowledge gap with metadata for prioritization.""" + gap_id: str gap_type: str # "entity_missing", "relationship_missing", "attention_uncertainty" severity: GapSeverity @@ -53,6 +55,7 @@ class KnowledgeGap: class AttentionConfig(BaseModel): """Configuration for attention-based gap detection.""" + entropy_threshold_low: float = Field(0.5, ge=0.0, le=2.0) entropy_threshold_high: float = Field(1.5, ge=0.0, le=2.0) min_attention_span_tokens: int = Field(3, ge=1) @@ -65,6 +68,7 @@ class AttentionConfig(BaseModel): ```python class SubgraphEntry(BaseModel): """Represents a cached subgraph entry.""" + entity_id: str neighbors: dict[str, dict[str, Any]] # neighbor_id -> properties relationship_types: dict[str, list[str]] # rel_type -> [neighbor_ids] @@ -74,6 +78,7 @@ class SubgraphEntry(BaseModel): class CacheMetrics(BaseModel): """Metrics tracking cache performance.""" + cache_hits: int = 0 cache_misses: int = 0 total_warming_calls: int = 0 @@ -92,6 +97,7 @@ class CacheMetrics(BaseModel): ```python class ReasoningPhase(str, Enum): """Phases of the reasoning cycle.""" + ACTION = "action" THINK = "think" MEMORY = "memory" @@ -101,6 +107,7 @@ class ReasoningPhase(str, Enum): @dataclass class ActionProposal: """Proposed action with rationale.""" + action_description: str action_params: dict[str, Any] confidence_score: float # 0.0 to 1.0 @@ -112,6 +119,7 @@ class ActionProposal: @dataclass class ThinkingOutput: """Output from thinking phase.""" + goal_progress_assessment: str moves_toward_goal: bool identified_gaps: list[str] @@ -123,6 +131,7 @@ class ThinkingOutput: @dataclass class MemoryContext: """Retrieved and warmed memory context.""" + retrieved_entities: dict[str, Any] entity_relationships: dict[str, set[str]] cache_hit_ratio: float @@ -137,10 +146,7 @@ Three detection strategies: ```python async def _detect_attention_gaps( - self, - attention_weights: np.ndarray, - layer_idx: Optional[int], - head_idx: Optional[int] + self, attention_weights: np.ndarray, layer_idx: Optional[int], head_idx: Optional[int] ) -> list[KnowledgeGap]: """ Detect gaps based on attention entropy analysis. @@ -154,8 +160,7 @@ async def _detect_attention_gaps( # Dynamic threshold based on percentile of history if len(self._entropy_history) > 10: percentile_value = np.percentile( - self._entropy_history, - self.config.attention_config.entropy_percentile_for_gap + self._entropy_history, self.config.attention_config.entropy_percentile_for_gap ) if entropy > percentile_value: @@ -167,9 +172,7 @@ async def _detect_attention_gaps( ```python async def _detect_entity_gaps( - self, - mentioned_entities: list[str], - entity_memory_graph: dict[str, set[str]] + self, mentioned_entities: list[str], entity_memory_graph: dict[str, set[str]] ) -> list[KnowledgeGap]: """ Detect gaps based on missing or incomplete entity references. @@ -183,9 +186,7 @@ async def _detect_entity_gaps( ```python async def _detect_relationship_gaps( - self, - mentioned_entities: list[str], - entity_memory_graph: dict[str, set[str]] + self, mentioned_entities: list[str], entity_memory_graph: dict[str, set[str]] ) -> list[KnowledgeGap]: """ Detect missing relationships between mentioned entities. @@ -249,16 +250,11 @@ proposal vs goal cache action ### Prometheus Metrics ```python -self.gap_detection_count = Counter( - 'gap_detection_count', 'Total gaps detected', ['gap_type'] -) +self.gap_detection_count = Counter("gap_detection_count", "Total gaps detected", ["gap_type"]) self.attention_entropy_histogram = Histogram( - 'attention_entropy_values', 'Attention entropy measurements', - buckets=[0.0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 2.0] -) -self.gap_detector_latency = Histogram( - 'gap_detector_latency_ms', 'Time to detect gaps' + "attention_entropy_values", "Attention entropy measurements", buckets=[0.0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 2.0] ) +self.gap_detector_latency = Histogram("gap_detector_latency_ms", "Time to detect gaps") ``` ### Target Metrics diff --git a/agents/cursor/perplexity_research_results/01-20-2026-langgraph-checkpoint-postgres/langgraph-checkpoint-postgres-best-practices.md b/agents/cursor/perplexity_research_results/01-20-2026-langgraph-checkpoint-postgres/langgraph-checkpoint-postgres-best-practices.md index 23854cfc..a422b4eb 100644 --- a/agents/cursor/perplexity_research_results/01-20-2026-langgraph-checkpoint-postgres/langgraph-checkpoint-postgres-best-practices.md +++ b/agents/cursor/perplexity_research_results/01-20-2026-langgraph-checkpoint-postgres/langgraph-checkpoint-postgres-best-practices.md @@ -145,13 +145,7 @@ class L9AsyncPostgresCheckpointer(AsyncPostgresSaver): self.base_retry_delay = base_retry_delay self._pool = conn_pool - async def _execute_with_retry( - self, - operation_name: str, - operation_func, - *args: Any, - **kwargs: Any - ) -> Any: + async def _execute_with_retry(self, operation_name: str, operation_func, *args: Any, **kwargs: Any) -> Any: """Execute checkpoint operation with exponential backoff retry.""" last_exception = None @@ -170,7 +164,7 @@ class L9AsyncPostgresCheckpointer(AsyncPostgresSaver): except Exception as e: last_exception = e - delay = self.base_retry_delay * (2 ** attempt) + delay = self.base_retry_delay * (2**attempt) logger.warning( "checkpoint_operation_retry", @@ -196,34 +190,17 @@ class L9AsyncPostgresCheckpointer(AsyncPostgresSaver): await self._execute_with_retry("setup", super().setup) logger.info("checkpoint_schema_initialized") - async def aget_tuple( - self, config: Dict[str, Any] - ) -> Optional[CheckpointTuple]: + async def aget_tuple(self, config: Dict[str, Any]) -> Optional[CheckpointTuple]: """Get checkpoint tuple with retry logic.""" - return await self._execute_with_retry( - "aget_tuple", - super().aget_tuple, - config - ) + return await self._execute_with_retry("aget_tuple", super().aget_tuple, config) async def aput( - self, - config: Dict[str, Any], - checkpoint: Checkpoint, - metadata: Dict[str, Any], - new_versions: Dict[str, Any] + self, config: Dict[str, Any], checkpoint: Checkpoint, metadata: Dict[str, Any], new_versions: Dict[str, Any] ) -> Dict[str, Any]: """Put checkpoint with retry logic and observability.""" start_time = datetime.utcnow() - result = await self._execute_with_retry( - "aput", - super().aput, - config, - checkpoint, - metadata, - new_versions - ) + result = await self._execute_with_retry("aput", super().aput, config, checkpoint, metadata, new_versions) duration_ms = (datetime.utcnow() - start_time).total_seconds() * 1000 @@ -307,32 +284,24 @@ class L9ThreadIDBuilder: """Build composite thread IDs for L9 multi-tenant isolation.""" @staticmethod - def build_thread_id( - tenant_id: str, - user_id: str, - session_id: Optional[str] = None - ) -> str: + def build_thread_id(tenant_id: str, user_id: str, session_id: Optional[str] = None) -> str: """Build hierarchical thread ID for tenant isolation.""" import uuid + session_id = session_id or str(uuid.uuid4()) return f"{tenant_id}:user:{user_id}:session:{session_id}" @staticmethod def build_config( - tenant_id: str, - user_id: str, - session_id: Optional[str] = None, - checkpoint_ns: str = "" + tenant_id: str, user_id: str, session_id: Optional[str] = None, checkpoint_ns: str = "" ) -> Dict[str, Any]: """Build complete config dict for graph execution.""" return { "configurable": { - "thread_id": L9ThreadIDBuilder.build_thread_id( - tenant_id, user_id, session_id - ), + "thread_id": L9ThreadIDBuilder.build_thread_id(tenant_id, user_id, session_id), "checkpoint_ns": f"{tenant_id}:{checkpoint_ns}", "tenant_id": tenant_id, - "user_id": user_id + "user_id": user_id, } } ``` @@ -344,10 +313,10 @@ class L9ThreadIDBuilder: ```python # Recommended pool settings for L9 production POOL_CONFIG = { - "min_size": 2, # Baseline connections - "max_size": 10, # Peak concurrent checkpoints - "timeout": 30.0, # Connection acquisition timeout - "max_idle": 300, # Max idle time before connection closed + "min_size": 2, # Baseline connections + "max_size": 10, # Peak concurrent checkpoints + "timeout": 30.0, # Connection acquisition timeout + "max_idle": 300, # Max idle time before connection closed "max_lifetime": 3600, # Max connection lifetime } ``` diff --git a/agents/cursor/prompts/gmp_examples/GMP-Action-Add-Learning-API-Routes.md b/agents/cursor/prompts/gmp_examples/GMP-Action-Add-Learning-API-Routes.md index b97fb6b4..625a7f72 100644 --- a/agents/cursor/prompts/gmp_examples/GMP-Action-Add-Learning-API-Routes.md +++ b/agents/cursor/prompts/gmp_examples/GMP-Action-Add-Learning-API-Routes.md @@ -116,10 +116,10 @@ class AnalyticsResponse(BaseModel): # Dependency to get engine def get_gmp_engine() -> GMPMetaLearningEngine: from api.server import gmp_learning_engine + if gmp_learning_engine is None: raise HTTPException( - status_code=503, - detail="GMP Learning Engine not initialized. Set L9_GMP_LEARNING_ENABLED=true" + status_code=503, detail="GMP Learning Engine not initialized. Set L9_GMP_LEARNING_ENABLED=true" ) return gmp_learning_engine @@ -128,19 +128,19 @@ def get_gmp_engine() -> GMPMetaLearningEngine: LEVEL_INFO = { "L2": { "description": "Constrained Execution", - "capabilities": ["locked_todo_plans", "static_audit", "no_learning"] + "capabilities": ["locked_todo_plans", "static_audit", "no_learning"], }, "L3": { "description": "Adaptive Execution", - "capabilities": ["adaptive_todos", "failure_recovery", "pattern_matching"] + "capabilities": ["adaptive_todos", "failure_recovery", "pattern_matching"], }, "L4": { "description": "Meta-Strategic Execution", - "capabilities": ["architectural_reasoning", "optimization_suggestions", "cross_gmp_analysis"] + "capabilities": ["architectural_reasoning", "optimization_suggestions", "cross_gmp_analysis"], }, "L5": { "description": "Fully Autonomous", - "capabilities": ["autonomous_goal", "self_healing", "proactive_improvements"] + "capabilities": ["autonomous_goal", "self_healing", "proactive_improvements"], }, } @@ -154,9 +154,7 @@ async def get_autonomy_level(engine: GMPMetaLearningEngine = Depends(get_gmp_eng info = LEVEL_INFO.get(level.value, LEVEL_INFO["L2"]) return AutonomyLevelResponse( - current_level=level.value, - description=info["description"], - capabilities=info["capabilities"] + current_level=level.value, description=info["description"], capabilities=info["capabilities"] ) @@ -174,7 +172,7 @@ async def get_graduation_status(engine: GMPMetaLearningEngine = Depends(get_gmp_ can_graduate=can_graduate, reason=reason, current_level=current.value, - next_level=next_level_map.get(current.value) + next_level=next_level_map.get(current.value), ) @@ -189,10 +187,7 @@ async def graduate_to_next_level(engine: GMPMetaLearningEngine = Depends(get_gmp next_level_map = {"L2": "L3", "L3": "L4", "L4": "L5", "L5": None} return GraduationStatusResponse( - can_graduate=success, - reason=message, - current_level=current.value, - next_level=next_level_map.get(current.value) + can_graduate=success, reason=message, current_level=current.value, next_level=next_level_map.get(current.value) ) @@ -214,7 +209,7 @@ async def get_heuristics(engine: GMPMetaLearningEngine = Depends(get_gmp_engine) "supporting_gmps": len(h.supporting_gmp_ids), } for h in heuristics - ] + ], ) @@ -230,7 +225,7 @@ async def get_analytics(engine: GMPMetaLearningEngine = Depends(get_gmp_engine)) avg_confidence=0.0, error_rate=0.0, pass_rate=0.0, - by_task_type={} + by_task_type={}, ) return AnalyticsResponse( @@ -239,15 +234,12 @@ async def get_analytics(engine: GMPMetaLearningEngine = Depends(get_gmp_engine)) avg_confidence=stats["avg_confidence"], error_rate=stats["error_rate"], pass_rate=stats["pass_rate"], - by_task_type=stats.get("by_task_type", {}) + by_task_type=stats.get("by_task_type", {}), ) @router.post("/log-execution") -async def log_execution( - result: GMPExecutionResult, - engine: GMPMetaLearningEngine = Depends(get_gmp_engine) -): +async def log_execution(result: GMPExecutionResult, engine: GMPMetaLearningEngine = Depends(get_gmp_engine)): """Log a GMP execution result (internal use).""" success = await engine.log_execution(result) @@ -264,7 +256,7 @@ async def log_execution( "current_level": metrics.current_level.value, "perfect_executions": metrics.perfect_executions_l2, "l2_to_l3_ready": metrics.l2_to_l3_ready, - } + }, } @@ -281,7 +273,7 @@ async def trigger_heuristic_generation(engine: GMPMetaLearningEngine = Depends(g "confidence": h.confidence, } for h in heuristics - ] + ], } ``` diff --git a/agents/cursor/prompts/gmp_examples/GMP-Action-Create-Learning-Tests.md b/agents/cursor/prompts/gmp_examples/GMP-Action-Create-Learning-Tests.md index 48549573..45174e9e 100644 --- a/agents/cursor/prompts/gmp_examples/GMP-Action-Create-Learning-Tests.md +++ b/agents/cursor/prompts/gmp_examples/GMP-Action-Create-Learning-Tests.md @@ -77,6 +77,7 @@ from core.gmp.meta_learning_engine import ( # PYDANTIC MODEL TESTS # ============================================================================ + class TestAutonomyLevel: """Tests for AutonomyLevel enum.""" @@ -104,7 +105,7 @@ class TestGMPExecutionResult: todo_count=5, execution_minutes=30.0, final_confidence=95.0, - audit_result="PASS" + audit_result="PASS", ) assert result.gmp_id == "GMP-TEST-001" assert result.error_count == 0 # Default @@ -119,7 +120,7 @@ class TestGMPExecutionResult: todo_count=5, execution_minutes=30.0, final_confidence=150.0, # Invalid - audit_result="PASS" + audit_result="PASS", ) def test_invalid_todo_count_negative(self): @@ -131,7 +132,7 @@ class TestGMPExecutionResult: todo_count=-1, # Invalid execution_minutes=30.0, final_confidence=95.0, - audit_result="PASS" + audit_result="PASS", ) def test_defaults_populated(self): @@ -142,7 +143,7 @@ class TestGMPExecutionResult: todo_count=1, execution_minutes=1.0, final_confidence=100.0, - audit_result="PASS" + audit_result="PASS", ) assert result.error_types == [] assert result.files_modified == [] @@ -161,7 +162,7 @@ class TestLearnedHeuristic: condition="if x > 10", recommendation="do something", confidence=0.85, - impact_estimate="faster" + impact_estimate="faster", ) assert h.pattern_text == "Test pattern" assert h.confidence == 0.85 @@ -176,24 +177,20 @@ class TestLearnedHeuristic: condition="x", recommendation="y", confidence=1.5, # Invalid > 1 - impact_estimate="faster" + impact_estimate="faster", ) def test_heuristic_hashable(self): """Test that heuristics can be used in sets.""" h1 = LearnedHeuristic( - pattern_text="Same pattern", - condition="x", - recommendation="y", - confidence=0.5, - impact_estimate="z" + pattern_text="Same pattern", condition="x", recommendation="y", confidence=0.5, impact_estimate="z" ) h2 = LearnedHeuristic( pattern_text="Same pattern", condition="different", recommendation="different", confidence=0.9, - impact_estimate="different" + impact_estimate="different", ) # Same pattern_text = same hash assert hash(h1) == hash(h2) @@ -218,6 +215,7 @@ class TestAutonomyGraduationMetrics: # CORRELATION FUNCTION TEST # ============================================================================ + class TestCorrelation: """Tests for correlation calculation.""" @@ -260,6 +258,7 @@ class TestCorrelation: # AUTONOMY CONTROLLER LOGIC TESTS # ============================================================================ + class TestAutonomyControllerLogic: """Tests for AutonomyController business logic (no DB).""" @@ -295,6 +294,7 @@ class TestAutonomyControllerLogic: # GRADUATION CRITERIA TESTS # ============================================================================ + class TestGraduationCriteria: """Tests for graduation prerequisite logic.""" @@ -327,7 +327,7 @@ class TestGraduationCriteria: execution_minutes=30.0, error_count=0, final_confidence=95.0, - audit_result="PASS" + audit_result="PASS", ) # Not perfect: has errors @@ -338,7 +338,7 @@ class TestGraduationCriteria: execution_minutes=30.0, error_count=1, final_confidence=95.0, - audit_result="PASS" + audit_result="PASS", ) # Not perfect: low confidence @@ -349,7 +349,7 @@ class TestGraduationCriteria: execution_minutes=30.0, error_count=0, final_confidence=90.0, - audit_result="PASS" + audit_result="PASS", ) # Not perfect: failed @@ -360,7 +360,7 @@ class TestGraduationCriteria: execution_minutes=30.0, error_count=0, final_confidence=95.0, - audit_result="FAIL" + audit_result="FAIL", ) # Check criteria @@ -377,6 +377,7 @@ class TestGraduationCriteria: # INTEGRATION TEST MARKERS # ============================================================================ + @pytest.mark.integration class TestEngineIntegration: """Integration tests requiring actual database (skipped by default).""" diff --git a/agents/cursor/prompts/gmp_examples/GMP-Action-Wire-Learning-Engine.md b/agents/cursor/prompts/gmp_examples/GMP-Action-Wire-Learning-Engine.md index 4194400d..f3cdbeef 100644 --- a/agents/cursor/prompts/gmp_examples/GMP-Action-Wire-Learning-Engine.md +++ b/agents/cursor/prompts/gmp_examples/GMP-Action-Wire-Learning-Engine.md @@ -85,6 +85,7 @@ from core.gmp import GMPMetaLearningEngine gmp_learning_engine: Optional[GMPMetaLearningEngine] = None + @asynccontextmanager async def lifespan(app: FastAPI): # ... existing init ... diff --git a/docs/ACTION ITEMS.MD b/docs/ACTION ITEMS.md similarity index 100% rename from docs/ACTION ITEMS.MD rename to docs/ACTION ITEMS.md diff --git a/docs/GRAPH-architecture.md b/docs/GRAPH-architecture.md index 9c81db49..6e9c84e0 100644 --- a/docs/GRAPH-architecture.md +++ b/docs/GRAPH-architecture.md @@ -112,12 +112,7 @@ Gates are **hard filters** that entities MUST pass. Each returns `GateResult`: ```python gate_result = gate_evaluator.evaluate_gate( - gate=WhereGate.MATERIAL_MATCH, - entity_id='company_123', - context={ - 'required_material': 'HDPE', - 'min_products': 3 - } + gate=WhereGate.MATERIAL_MATCH, entity_id="company_123", context={"required_material": "HDPE", "min_products": 3} ) if not gate_result.passed: @@ -161,11 +156,7 @@ facility_score = min(1.0, facility_count / 3.0) product_score = min(1.0, product_count / 5.0) material_diversity = min(1.0, material_count / 4.0) -capability_score = ( - facility_score * 0.4 + - product_score * 0.3 + - material_diversity * 0.3 -) +capability_score = facility_score * 0.4 + product_score * 0.3 + material_diversity * 0.3 ``` ### 2. COMPATIBILITY — "Does it fit our requirements?" @@ -191,11 +182,7 @@ RETURN c.id as entity_id, **Scoring Logic:** ```python -compatibility_score = ( - grade_match_score * 0.5 + - product_count_score * 0.2 + - mfi_score * 0.3 -) +compatibility_score = grade_match_score * 0.5 + product_count_score * 0.2 + mfi_score * 0.3 ``` ### 3. CAPACITY — "Can they handle the volume?" @@ -257,11 +244,7 @@ success_score = success_rate # Primary indicator response_score = 1.0 / (1.0 + avg_response_time / 24.0) consistency_score = 1.0 / (1.0 + response_consistency / 12.0) -commitment_score = ( - success_score * 0.6 + - response_score * 0.25 + - consistency_score * 0.15 -) +commitment_score = success_score * 0.6 + response_score * 0.25 + consistency_score * 0.15 ``` --- @@ -281,32 +264,18 @@ if not required_pass: return EntityScore(composite_score=0.0) # Step 3: Score dimensions -dimension_scores = { - dim: score_dimension(dim, entity_id, context) - for dim in ScoringDimension -} +dimension_scores = {dim: score_dimension(dim, entity_id, context) for dim in ScoringDimension} # Step 4: Weighted composite -weights = { - CAPABILITY: 0.25, - COMPATIBILITY: 0.30, - CAPACITY: 0.25, - COMMITMENT: 0.20 -} +weights = {CAPABILITY: 0.25, COMPATIBILITY: 0.30, CAPACITY: 0.25, COMMITMENT: 0.20} -composite = sum( - dimension_scores[dim].score * weights[dim] - for dim in ScoringDimension -) +composite = sum(dimension_scores[dim].score * weights[dim] for dim in ScoringDimension) # Step 5: Apply gate pass rate modifier composite *= gate_pass_rate return EntityScore( - entity_id=entity_id, - gate_results=gate_results, - dimension_scores=dimension_scores, - composite_score=composite + entity_id=entity_id, gate_results=gate_results, dimension_scores=dimension_scores, composite_score=composite ) ``` @@ -386,11 +355,11 @@ entity_score = engine.evaluate_entity(entity_id, gates, context) ```python # After engagement, capture actual outcome outcome = { - 'entity_id': entity_id, - 'predicted_score': 0.85, - 'actual_outcome': 'success', # or 'failure' - 'actual_score': 0.95, # Measured performance - 'timestamp': datetime.utcnow() + "entity_id": entity_id, + "predicted_score": 0.85, + "actual_outcome": "success", # or 'failure' + "actual_score": 0.95, # Measured performance + "timestamp": datetime.utcnow(), } ``` @@ -413,13 +382,13 @@ if error > 0.1: # Under-predicted if abs(error) > 0.2: inference_packet = PacketEnvelope( payload={ - 'entity_id': entity_id, - 'inference_type': 'capability_reassessment', - 'reason': f'prediction_error={error:.2f}', - 'priority': 'high' + "entity_id": entity_id, + "inference_type": "capability_reassessment", + "reason": f"prediction_error={error:.2f}", + "priority": "high", }, - source='GRAPH', - destination='ENRICH' + source="GRAPH", + destination="ENRICH", ) enrich_service.submit(inference_packet) ``` @@ -440,15 +409,11 @@ if abs(error) > 0.2: ### Feature Vector Extraction ```python feature_vector = { - 'entity_id': 'company_123', - 'entity_types': ['Company', 'Manufacturer'], - 'out_degree': 45, # Number of outgoing relationships - 'relationship_types': ['PRODUCES', 'OPERATES', 'INTERACTED_WITH'], - 'attributes': { - 'industry': 'plastics_recycling', - 'employee_count': 250, - 'revenue': 15000000 - } + "entity_id": "company_123", + "entity_types": ["Company", "Manufacturer"], + "out_degree": 45, # Number of outgoing relationships + "relationship_types": ["PRODUCES", "OPERATES", "INTERACTED_WITH"], + "attributes": {"industry": "plastics_recycling", "employee_count": 250, "revenue": 15000000}, } ``` @@ -456,9 +421,9 @@ feature_vector = { ```python # Prepare triples triples = [ - ('company_123', 'PRODUCES', 'product_456'), - ('company_123', 'OPERATES', 'facility_789'), - ('product_456', 'CONTAINS', 'material_hdpe') + ("company_123", "PRODUCES", "product_456"), + ("company_123", "OPERATES", "facility_789"), + ("product_456", "CONTAINS", "material_hdpe"), ] # Train CompoundE3D @@ -466,7 +431,7 @@ model = CompoundE3D(dim=256) model.train(triples, epochs=100) # Get embedding -embedding = model.embed('company_123') # [256-dim vector] +embedding = model.embed("company_123") # [256-dim vector] ``` ### Use Cases @@ -549,9 +514,10 @@ embedding = model.embed('company_123') # [256-dim vector] @dataclass class PacketEnvelope: """Immutable communication contract""" + payload: Dict[str, any] - source: str # Originating service - destination: str # Target service + source: str # Originating service + destination: str # Target service packet_id: str = field(default_factory=uuid4) timestamp: datetime = field(default_factory=datetime.utcnow) provenance: List[str] = field(default_factory=list) @@ -562,18 +528,14 @@ class PacketEnvelope: # ENRICH completes convergence, sends enriched entity to GRAPH packet = PacketEnvelope( payload={ - 'entity_id': 'company_123', - 'entity_type': 'Company', - 'enrichment_data': { - 'industry': 'plastics_recycling', - 'facility_count': 3, - 'material_types': ['HDPE', 'LDPE'] - }, - 'confidence': 0.92, - 'convergence_passes': 3 + "entity_id": "company_123", + "entity_type": "Company", + "enrichment_data": {"industry": "plastics_recycling", "facility_count": 3, "material_types": ["HDPE", "LDPE"]}, + "confidence": 0.92, + "convergence_passes": 3, }, - source='ENRICH', - destination='GRAPH' + source="ENRICH", + destination="GRAPH", ) graph_service.ingest(packet) @@ -584,19 +546,14 @@ graph_service.ingest(packet) # GRAPH completes scoring, sends results to SCORE packet = PacketEnvelope( payload={ - 'entity_id': 'company_123', - 'composite_score': 0.85, - 'dimension_scores': { - 'capability': 0.80, - 'compatibility': 0.92, - 'capacity': 0.78, - 'commitment': 0.88 - }, - 'gates_passed': 12, - 'gates_failed': 2 + "entity_id": "company_123", + "composite_score": 0.85, + "dimension_scores": {"capability": 0.80, "compatibility": 0.92, "capacity": 0.78, "commitment": 0.88}, + "gates_passed": 12, + "gates_failed": 2, }, - source='GRAPH', - destination='SCORE' + source="GRAPH", + destination="SCORE", ) score_service.rank(packet) @@ -711,33 +668,33 @@ Each tier naturally upsells to the next: ### Context (from Sales Request) ```python context = { - 'required_material': 'HDPE', - 'required_grade': 'post-consumer', - 'target_mfi': 0.8, - 'required_capacity': 50000, # kg/month - 'max_tier': 2, - 'lookback_days': 365 + "required_material": "HDPE", + "required_grade": "post-consumer", + "target_mfi": 0.8, + "required_capacity": 50000, # kg/month + "max_tier": 2, + "lookback_days": 365, } ``` ### Evaluation ```python gates = [ - WhereGate.MATERIAL_MATCH, # ✅ Produces HDPE - WhereGate.GRADE_COMPATIBILITY, # ✅ Post-consumer grade - WhereGate.MFI_RANGE_MATCH, # ✅ MFI 0.5-1.5 (target 0.8) - WhereGate.FACILITY_TIER, # ✅ Tier 2 facilities - WhereGate.CAPACITY_THRESHOLD, # ✅ 150K > 50K required - WhereGate.COMPLIANCE_STATUS # ✅ ISO + FDA certified + WhereGate.MATERIAL_MATCH, # ✅ Produces HDPE + WhereGate.GRADE_COMPATIBILITY, # ✅ Post-consumer grade + WhereGate.MFI_RANGE_MATCH, # ✅ MFI 0.5-1.5 (target 0.8) + WhereGate.FACILITY_TIER, # ✅ Tier 2 facilities + WhereGate.CAPACITY_THRESHOLD, # ✅ 150K > 50K required + WhereGate.COMPLIANCE_STATUS, # ✅ ISO + FDA certified ] -score = engine.evaluate_entity('company_123', gates, context) +score = engine.evaluate_entity("company_123", gates, context) ``` ### Output ```python EntityScore( - entity_id='company_123', + entity_id="company_123", composite_score=0.87, gates_passed=6, gates_failed=0, @@ -745,30 +702,22 @@ EntityScore( CAPABILITY: ScoringResult(score=0.85, confidence=0.90), COMPATIBILITY: ScoringResult(score=0.92, confidence=0.88), CAPACITY: ScoringResult(score=0.81, confidence=0.85), - COMMITMENT: ScoringResult(score=0.88, confidence=0.92) - } + COMMITMENT: ScoringResult(score=0.88, confidence=0.92), + }, ) ``` ### Downstream Action ```python # Send to SCORE for ranking -score_packet = PacketEnvelope( - payload={'entity_score': score.to_dict()}, - source='GRAPH', - destination='SCORE' -) +score_packet = PacketEnvelope(payload={"entity_score": score.to_dict()}, source="GRAPH", destination="SCORE") # If high score, send to ROUTE for assignment if score.composite_score > 0.8: route_packet = PacketEnvelope( - payload={ - 'entity_id': 'company_123', - 'score': 0.87, - 'recommended_action': 'assign_to_senior_rep' - }, - source='GRAPH', - destination='ROUTE' + payload={"entity_id": "company_123", "score": 0.87, "recommended_action": "assign_to_senior_rep"}, + source="GRAPH", + destination="ROUTE", ) ``` diff --git a/docs/INFERENCE_ENGINE_KG_ROADMAP.md b/docs/INFERENCE_ENGINE_KG_ROADMAP.md index 0875f1b7..ef8042bf 100644 --- a/docs/INFERENCE_ENGINE_KG_ROADMAP.md +++ b/docs/INFERENCE_ENGINE_KG_ROADMAP.md @@ -206,10 +206,10 @@ class CompGCNLayer(nn.Module): def forward(self, x, rel, edge_index, edge_type, basis, coeff): out = torch.zeros_like(x) for r in range(len(rel)): - mask = (edge_type == r) + mask = edge_type == r src, dst = edge_index[:, mask] composed = circular_corr(x[src], rel[r].expand(len(src), -1)) # φ(x_u, z_r) - W_r = torch.einsum('b,boi->oi', coeff[r], basis) # basis decomp + W_r = torch.einsum("b,boi->oi", coeff[r], basis) # basis decomp out[dst] += W_r @ composed.t() # CRITICAL: also update relation embeddings via reverse aggregation return F.relu(out), updated_rel @@ -222,19 +222,20 @@ def bellman_ford(h_prev, graph, W_r, query_rel, T=6): h_next = torch.zeros_like(h_prev) for v in graph.nodes: msgs = [] - for (u, r, v_) in graph.incoming_edges(v): - if v_ == v and random() > 0.1: # 10% edge dropout - w_q = W_r[r] @ query_rel + b_r[r] # relation-only edge repr (inductive) + for u, r, v_ in graph.incoming_edges(v): + if v_ == v and random() > 0.1: # 10% edge dropout + w_q = W_r[r] @ query_rel + b_r[r] # relation-only edge repr (inductive) msgs.append(rotate(h_prev[u], w_q)) # RotatE MESSAGE if msgs: h_next[v] = pna_aggregate(msgs, degree=len(msgs)) h_prev = h_next return h_prev + def pna_aggregate(messages, degree): M = torch.stack(messages) aggs = torch.cat([M.mean(0), M.max(0)[0], M.sum(0), M.std(0)]) # 4 aggregators - return mlp(aggs * learned_scalers * math.log(degree + 1)) # degree scaling + return mlp(aggs * learned_scalers * math.log(degree + 1)) # degree scaling ``` ### CompoundE3D Operator (Milestone 4) @@ -244,13 +245,17 @@ class CompoundE3DOperator(nn.Module): h_blocks = h.view(-1, 3) # d=256 → 85 blocks of 3×3 out = [] for i, v in enumerate(h_blocks): - v = v @ self.H_shear[rel_idx, i] # shear + v = v @ self.H_shear[rel_idx, i] # shear n = F.normalize(self.F_normal[rel_idx, i], dim=-1) - v = v - 2 * (v @ n) * n # reflection - v = v * F.softplus(self.S_log[rel_idx, i]) # scaling (positive) - v = v @ self._quaternion_to_rotation( # SO(3) rotation - F.normalize(self.R_quat[rel_idx, i], dim=-1)).t() - v = v + self.T[rel_idx, i] # translation + v = v - 2 * (v @ n) * n # reflection + v = v * F.softplus(self.S_log[rel_idx, i]) # scaling (positive) + v = ( + v + @ self._quaternion_to_rotation( # SO(3) rotation + F.normalize(self.R_quat[rel_idx, i], dim=-1) + ).t() + ) + v = v + self.T[rel_idx, i] # translation out.append(v) return torch.stack(out).view(-1) ``` @@ -259,9 +264,10 @@ class CompoundE3DOperator(nn.Module): ```python # Sanitize external API response def sanitize(results, session_salt): - return [{'id': hmac(r.id, session_salt), 'score': round(r.score, 3)} for r in results] + return [{"id": hmac(r.id, session_salt), "score": round(r.score, 3)} for r in results] # Never return: dimension_scores, gates_passed, explanation paths, neighbor IDs + # Rate limit high-novelty sessions async def match_endpoint(request, session_id=Header(...)): if novelty_tracker.compute_novelty(request) > 0.3: @@ -270,12 +276,14 @@ async def match_endpoint(request, session_id=Header(...)): traversal_monitor.check_query(session_id, request.queried_entities) return sanitize(await run_matching(request), session_salt=session_id) + # Watermark: 0.1% phantom triples per session for attribution def inject_watermark(graph, session_id): rng = np.random.RandomState(int(sha256(session_id.encode()).hexdigest(), 16) % 10_000) for _ in range(max(1, len(graph.edges) // 1000)): - graph.add_edge(rng.choice(graph.nodes), rng.choice(graph.relations), - rng.choice(graph.nodes), watermark=session_id) + graph.add_edge( + rng.choice(graph.nodes), rng.choice(graph.relations), rng.choice(graph.nodes), watermark=session_id + ) ``` ### Active Enrichment Scheduler (M7) diff --git a/docs/L9_Contract_Enforcement_System.md b/docs/L9_Contract_Enforcement_System.md index 6c71a258..fd6282ba 100644 --- a/docs/L9_Contract_Enforcement_System.md +++ b/docs/L9_Contract_Enforcement_System.md @@ -124,6 +124,7 @@ import re, sys from pathlib import Path from dataclasses import dataclass + @dataclass class Violation: file: str @@ -134,160 +135,262 @@ class Violation: message: str remediation: str + RULES = [ # -- CONTRACT 3: CYPHER_SAFETY.md -- - {"id": "SEC-001", "contract": "CYPHER_SAFETY.md", "severity": "CRITICAL", - "pattern": r'f["\']\.\*MATCH\s\*\(.*\{[^$]', - "message": "Cypher label interpolation without sanitize_label()", - "remediation": "Use sanitize_label() for labels, $param for values", - "include_dirs": ["engine/"]}, - {"id": "SEC-002", "contract": "CYPHER_SAFETY.md", "severity": "CRITICAL", - "pattern": r'\beval\s\*\(', - "message": "eval() is banned - code injection risk", - "remediation": "Use operator dispatch table or ast.literal_eval()", - "exclude_dirs": ["tests/"]}, - {"id": "SEC-003", "contract": "CYPHER_SAFETY.md", "severity": "CRITICAL", - "pattern": r'\bexec\s\*\(', - "message": "exec() is banned - code injection risk", - "remediation": "Remove entirely", - "exclude_dirs": ["tests/"]}, - {"id": "SEC-004", "contract": "CYPHER_SAFETY.md", "severity": "CRITICAL", - "pattern": r'f["\']\.\*LIMIT\s\*\{', - "message": "LIMIT value interpolation - use $limit parameter", - "remediation": "LIMIT $limit with params={'limit': n}"}, - {"id": "SEC-005", "contract": "BANNED_PATTERNS.md", "severity": "CRITICAL", - "pattern": r'f["\']\.\*(?:SELECT|INSERT|UPDATE|DELETE)\s.*\{', - "message": "SQL string interpolation - use parameterized queries", - "remediation": "Use $1/$2 placeholders or ORM"}, - {"id": "SEC-006", "contract": "BANNED_PATTERNS.md", "severity": "CRITICAL", - "pattern": r'pickle\.loads?\s\*\(', - "message": "pickle banned - deserialization attack vector", - "remediation": "Use json.loads()"}, - {"id": "SEC-007", "contract": "BANNED_PATTERNS.md", "severity": "CRITICAL", - "pattern": r'yaml\.load\s\*\([^)]*\)\s*$', - "message": "yaml.load() without SafeLoader", - "remediation": "yaml.safe_load()"}, - + { + "id": "SEC-001", + "contract": "CYPHER_SAFETY.md", + "severity": "CRITICAL", + "pattern": r'f["\']\.\*MATCH\s\*\(.*\{[^$]', + "message": "Cypher label interpolation without sanitize_label()", + "remediation": "Use sanitize_label() for labels, $param for values", + "include_dirs": ["engine/"], + }, + { + "id": "SEC-002", + "contract": "CYPHER_SAFETY.md", + "severity": "CRITICAL", + "pattern": r"\beval\s\*\(", + "message": "eval() is banned - code injection risk", + "remediation": "Use operator dispatch table or ast.literal_eval()", + "exclude_dirs": ["tests/"], + }, + { + "id": "SEC-003", + "contract": "CYPHER_SAFETY.md", + "severity": "CRITICAL", + "pattern": r"\bexec\s\*\(", + "message": "exec() is banned - code injection risk", + "remediation": "Remove entirely", + "exclude_dirs": ["tests/"], + }, + { + "id": "SEC-004", + "contract": "CYPHER_SAFETY.md", + "severity": "CRITICAL", + "pattern": r'f["\']\.\*LIMIT\s\*\{', + "message": "LIMIT value interpolation - use $limit parameter", + "remediation": "LIMIT $limit with params={'limit': n}", + }, + { + "id": "SEC-005", + "contract": "BANNED_PATTERNS.md", + "severity": "CRITICAL", + "pattern": r'f["\']\.\*(?:SELECT|INSERT|UPDATE|DELETE)\s.*\{', + "message": "SQL string interpolation - use parameterized queries", + "remediation": "Use $1/$2 placeholders or ORM", + }, + { + "id": "SEC-006", + "contract": "BANNED_PATTERNS.md", + "severity": "CRITICAL", + "pattern": r"pickle\.loads?\s\*\(", + "message": "pickle banned - deserialization attack vector", + "remediation": "Use json.loads()", + }, + { + "id": "SEC-007", + "contract": "BANNED_PATTERNS.md", + "severity": "CRITICAL", + "pattern": r"yaml\.load\s\*\([^)]*\)\s*$", + "message": "yaml.load() without SafeLoader", + "remediation": "yaml.safe_load()", + }, # -- CONTRACT 4: ERROR_HANDLING.md -- - {"id": "ERR-001", "contract": "ERROR_HANDLING.md", "severity": "HIGH", - "pattern": r'except\s*:', - "message": "Bare except: clause", - "remediation": "except SpecificError as e:"}, - {"id": "ERR-002", "contract": "ERROR_HANDLING.md", "severity": "HIGH", - "pattern": r'except\s+\w+.*:\s*\n\s*pass', - "message": "Swallowed exception - except + pass", - "remediation": "Log and re-raise"}, - + { + "id": "ERR-001", + "contract": "ERROR_HANDLING.md", + "severity": "HIGH", + "pattern": r"except\s*:", + "message": "Bare except: clause", + "remediation": "except SpecificError as e:", + }, + { + "id": "ERR-002", + "contract": "ERROR_HANDLING.md", + "severity": "HIGH", + "pattern": r"except\s+\w+.*:\s*\n\s*pass", + "message": "Swallowed exception - except + pass", + "remediation": "Log and re-raise", + }, # -- CONTRACT 10: BANNED_PATTERNS.md (Architecture) -- - {"id": "ARCH-001", "contract": "BANNED_PATTERNS.md", "severity": "CRITICAL", - "pattern": r'from\s+fastapi\s+import', - "message": "FastAPI import in engine/ - chassis owns HTTP", - "remediation": "Register handlers in engine/handlers.py", - "include_dirs": ["engine/"]}, - {"id": "ARCH-002", "contract": "BANNED_PATTERNS.md", "severity": "CRITICAL", - "pattern": r'from\s+starlette\s+import', - "message": "Starlette import in engine/ - chassis owns middleware", - "remediation": "Remove", - "include_dirs": ["engine/"]}, - {"id": "ARCH-003", "contract": "BANNED_PATTERNS.md", "severity": "CRITICAL", - "pattern": r'import\s+uvicorn', - "message": "uvicorn import in engine/ - chassis owns ASGI", - "remediation": "Remove", - "include_dirs": ["engine/"]}, - + { + "id": "ARCH-001", + "contract": "BANNED_PATTERNS.md", + "severity": "CRITICAL", + "pattern": r"from\s+fastapi\s+import", + "message": "FastAPI import in engine/ - chassis owns HTTP", + "remediation": "Register handlers in engine/handlers.py", + "include_dirs": ["engine/"], + }, + { + "id": "ARCH-002", + "contract": "BANNED_PATTERNS.md", + "severity": "CRITICAL", + "pattern": r"from\s+starlette\s+import", + "message": "Starlette import in engine/ - chassis owns middleware", + "remediation": "Remove", + "include_dirs": ["engine/"], + }, + { + "id": "ARCH-003", + "contract": "BANNED_PATTERNS.md", + "severity": "CRITICAL", + "pattern": r"import\s+uvicorn", + "message": "uvicorn import in engine/ - chassis owns ASGI", + "remediation": "Remove", + "include_dirs": ["engine/"], + }, # -- CONTRACT 7: DEPENDENCY_INJECTION.md -- - {"id": "DI-001", "contract": "DEPENDENCY_INJECTION.md", "severity": "HIGH", - "pattern": r'from\s+fastapi\s+import\s+Depends', - "message": "FastAPI Depends in engine/ - chassis concern", - "remediation": "Use init_dependencies() pattern", - "include_dirs": ["engine/"]}, - + { + "id": "DI-001", + "contract": "DEPENDENCY_INJECTION.md", + "severity": "HIGH", + "pattern": r"from\s+fastapi\s+import\s+Depends", + "message": "FastAPI Depends in engine/ - chassis concern", + "remediation": "Use init_dependencies() pattern", + "include_dirs": ["engine/"], + }, # -- CONTRACT 12: DELEGATION_PROTOCOL.md -- - {"id": "DEL-001", "contract": "DELEGATION_PROTOCOL.md", "severity": "CRITICAL", - "pattern": r'httpx\.(post|get|put|delete|patch)\s*\(', - "message": "Raw HTTP to another node - use delegate_to_node()", - "remediation": "from l9.core.delegation import delegate_to_node", - "include_dirs": ["engine/"]}, - {"id": "DEL-002", "contract": "DELEGATION_PROTOCOL.md", "severity": "CRITICAL", - "pattern": r'requests\.(post|get|put|delete|patch)\s*\(', - "message": "Raw HTTP via requests - use delegate_to_node()", - "remediation": "from l9.core.delegation import delegate_to_node", - "include_dirs": ["engine/"]}, - + { + "id": "DEL-001", + "contract": "DELEGATION_PROTOCOL.md", + "severity": "CRITICAL", + "pattern": r"httpx\.(post|get|put|delete|patch)\s*\(", + "message": "Raw HTTP to another node - use delegate_to_node()", + "remediation": "from l9.core.delegation import delegate_to_node", + "include_dirs": ["engine/"], + }, + { + "id": "DEL-002", + "contract": "DELEGATION_PROTOCOL.md", + "severity": "CRITICAL", + "pattern": r"requests\.(post|get|put|delete|patch)\s*\(", + "message": "Raw HTTP via requests - use delegate_to_node()", + "remediation": "from l9.core.delegation import delegate_to_node", + "include_dirs": ["engine/"], + }, # -- CONTRACT 19: MEMORY_SUBSTRATE_ACCESS.md -- - {"id": "MEM-001", "contract": "MEMORY_SUBSTRATE_ACCESS.md", "severity": "CRITICAL", - "pattern": r'INSERT\s+INTO\s+packetstore', - "message": "Direct write to packetstore - use ingest_packet()", - "remediation": "from l9.memory.ingestion import ingest_packet", - "include_dirs": ["engine/"]}, - {"id": "MEM-002", "contract": "MEMORY_SUBSTRATE_ACCESS.md", "severity": "CRITICAL", - "pattern": r'INSERT\s+INTO\s+memory_embeddings', - "message": "Direct write to memory_embeddings - use ingest_packet()", - "remediation": "Embeddings generated by LangGraph DAG", - "include_dirs": ["engine/"]}, - + { + "id": "MEM-001", + "contract": "MEMORY_SUBSTRATE_ACCESS.md", + "severity": "CRITICAL", + "pattern": r"INSERT\s+INTO\s+packetstore", + "message": "Direct write to packetstore - use ingest_packet()", + "remediation": "from l9.memory.ingestion import ingest_packet", + "include_dirs": ["engine/"], + }, + { + "id": "MEM-002", + "contract": "MEMORY_SUBSTRATE_ACCESS.md", + "severity": "CRITICAL", + "pattern": r"INSERT\s+INTO\s+memory_embeddings", + "message": "Direct write to memory_embeddings - use ingest_packet()", + "remediation": "Embeddings generated by LangGraph DAG", + "include_dirs": ["engine/"], + }, # -- CONTRACT 20: SHARED_MODELS.md -- - {"id": "SHARED-001", "contract": "SHARED_MODELS.md", "severity": "HIGH", - "pattern": r'class\s+PacketEnvelope\s*\(', - "message": "Redefining PacketEnvelope - import from l9.core", - "remediation": "from l9.core.envelope import PacketEnvelope", - "include_dirs": ["engine/"]}, - {"id": "SHARED-002", "contract": "SHARED_MODELS.md", "severity": "HIGH", - "pattern": r'class\s+TenantContext\s*\(', - "message": "Redefining TenantContext - import from l9.core", - "remediation": "from l9.core.envelope import TenantContext", - "include_dirs": ["engine/"]}, - {"id": "SHARED-003", "contract": "SHARED_MODELS.md", "severity": "HIGH", - "pattern": r'class\s+ExecuteRequest\s*\(', - "message": "Redefining ExecuteRequest - import from l9.core", - "remediation": "from l9.core.contract import ExecuteRequest", - "include_dirs": ["engine/"]}, - + { + "id": "SHARED-001", + "contract": "SHARED_MODELS.md", + "severity": "HIGH", + "pattern": r"class\s+PacketEnvelope\s*\(", + "message": "Redefining PacketEnvelope - import from l9.core", + "remediation": "from l9.core.envelope import PacketEnvelope", + "include_dirs": ["engine/"], + }, + { + "id": "SHARED-002", + "contract": "SHARED_MODELS.md", + "severity": "HIGH", + "pattern": r"class\s+TenantContext\s*\(", + "message": "Redefining TenantContext - import from l9.core", + "remediation": "from l9.core.envelope import TenantContext", + "include_dirs": ["engine/"], + }, + { + "id": "SHARED-003", + "contract": "SHARED_MODELS.md", + "severity": "HIGH", + "pattern": r"class\s+ExecuteRequest\s*\(", + "message": "Redefining ExecuteRequest - import from l9.core", + "remediation": "from l9.core.contract import ExecuteRequest", + "include_dirs": ["engine/"], + }, # -- CONTRACT 18: OBSERVABILITY.md -- - {"id": "OBS-001", "contract": "OBSERVABILITY.md", "severity": "HIGH", - "pattern": r'structlog\.configure\s*\(', - "message": "Configuring structlog in engine - chassis does this", - "remediation": "Use logging.getLogger(__name__)", - "include_dirs": ["engine/"]}, - {"id": "OBS-002", "contract": "OBSERVABILITY.md", "severity": "HIGH", - "pattern": r'logging\.basicConfig\s*\(', - "message": "Configuring logging in engine - chassis does this", - "remediation": "Use logging.getLogger(__name__) only", - "include_dirs": ["engine/"]}, - + { + "id": "OBS-001", + "contract": "OBSERVABILITY.md", + "severity": "HIGH", + "pattern": r"structlog\.configure\s*\(", + "message": "Configuring structlog in engine - chassis does this", + "remediation": "Use logging.getLogger(__name__)", + "include_dirs": ["engine/"], + }, + { + "id": "OBS-002", + "contract": "OBSERVABILITY.md", + "severity": "HIGH", + "pattern": r"logging\.basicConfig\s*\(", + "message": "Configuring logging in engine - chassis does this", + "remediation": "Use logging.getLogger(__name__) only", + "include_dirs": ["engine/"], + }, # -- CONTRACT 6: PYDANTIC_YAML_MAPPING.md -- - {"id": "NAME-001", "contract": "PYDANTIC_YAML_MAPPING.md", "severity": "HIGH", - "pattern": r'Field\s*\(\s*alias\s*=', - "message": "Pydantic Field alias banned - snake_case everywhere", - "remediation": "Remove alias, use snake_case matching YAML key", - "include_dirs": ["engine/"]}, - + { + "id": "NAME-001", + "contract": "PYDANTIC_YAML_MAPPING.md", + "severity": "HIGH", + "pattern": r"Field\s*\(\s*alias\s*=", + "message": "Pydantic Field alias banned - snake_case everywhere", + "remediation": "Remove alias, use snake_case matching YAML key", + "include_dirs": ["engine/"], + }, # -- ZERO-STUB BUILD PROTOCOL -- - {"id": "STUB-001", "contract": "ZERO_STUB_BUILD_PROTOCOL.md", "severity": "CRITICAL", - "pattern": r'raise\s+NotImplementedError', - "message": "NotImplementedError stub - implement or DEFERRED.md", - "remediation": "Write implementation or document in DEFERRED.md", - "exclude_dirs": ["tests/"]}, - {"id": "STUB-002", "contract": "ZERO_STUB_BUILD_PROTOCOL.md", "severity": "HIGH", - "pattern": r'#\s*TODO', - "message": "TODO comment - implement or DEFERRED.md", - "remediation": "Implement or defer explicitly"}, - {"id": "STUB-003", "contract": "ZERO_STUB_BUILD_PROTOCOL.md", "severity": "HIGH", - "pattern": r'#\s*PLACEHOLDER', - "message": "PLACEHOLDER comment - implement or defer", - "remediation": "Write real code or DEFERRED.md"}, - + { + "id": "STUB-001", + "contract": "ZERO_STUB_BUILD_PROTOCOL.md", + "severity": "CRITICAL", + "pattern": r"raise\s+NotImplementedError", + "message": "NotImplementedError stub - implement or DEFERRED.md", + "remediation": "Write implementation or document in DEFERRED.md", + "exclude_dirs": ["tests/"], + }, + { + "id": "STUB-002", + "contract": "ZERO_STUB_BUILD_PROTOCOL.md", + "severity": "HIGH", + "pattern": r"#\s*TODO", + "message": "TODO comment - implement or DEFERRED.md", + "remediation": "Implement or defer explicitly", + }, + { + "id": "STUB-003", + "contract": "ZERO_STUB_BUILD_PROTOCOL.md", + "severity": "HIGH", + "pattern": r"#\s*PLACEHOLDER", + "message": "PLACEHOLDER comment - implement or defer", + "remediation": "Write real code or DEFERRED.md", + }, # -- CONTRACT 13: PACKET_TYPE_REGISTRY.md -- - {"id": "PKT-001", "contract": "PACKET_TYPE_REGISTRY.md", "severity": "HIGH", - "pattern": r'packet_type\s*[=:]\s*["\'"][A-Z]', - "message": "Uppercase packet_type - must be lowercase snake_case", - "remediation": "Check PACKET_TYPE_REGISTRY.md"}, - + { + "id": "PKT-001", + "contract": "PACKET_TYPE_REGISTRY.md", + "severity": "HIGH", + "pattern": r'packet_type\s*[=:]\s*["\'"][A-Z]', + "message": "Uppercase packet_type - must be lowercase snake_case", + "remediation": "Check PACKET_TYPE_REGISTRY.md", + }, # -- CONTRACT 17: ENV_VARS.md -- - {"id": "ENV-001", "contract": "ENV_VARS.md", "severity": "MEDIUM", - "pattern": r'os\.environ\[.(?:NEO4J_URI|NEO4J_URL|DATABASE_URL|REDIS_HOST|API_KEY).\]', - "message": "Non-standard env var name", - "remediation": "Use L9_* or ENGINE_* prefix per ENV_VARS.md"}, + { + "id": "ENV-001", + "contract": "ENV_VARS.md", + "severity": "MEDIUM", + "pattern": r"os\.environ\[.(?:NEO4J_URI|NEO4J_URL|DATABASE_URL|REDIS_HOST|API_KEY).\]", + "message": "Non-standard env var name", + "remediation": "Use L9_* or ENGINE_* prefix per ENV_VARS.md", + }, ] ``` diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index ed9c2ff5..baadd8aa 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -230,7 +230,7 @@ w_new_dimension = 0.10 # ❌ causes sum = 0.95 # After (sum = 0.95 → 0.90): w_structural = 0.28 # reduced -w_geo = 0.23 # reduced +w_geo = 0.23 # reduced w_reinforcement = 0.19 # reduced w_freshness = 0.10 # unchanged w_new_dimension = 0.10 # new @@ -255,6 +255,7 @@ This violates **Contract C-001** (Single Ingress). Engine NEVER imports FastAPI. ```python # ❌ WRONG (in engine/) from fastapi import HTTPException + raise HTTPException(status_code=400, detail="Invalid gate") # ✅ CORRECT (in engine/) @@ -263,6 +264,7 @@ raise ValueError(msg) # ✅ CORRECT (in chassis/ only) from fastapi import HTTPException + raise HTTPException(status_code=400, detail="Invalid gate") ``` diff --git a/docs/contracts/DEPENDENCY_INJECTION.md b/docs/contracts/DEPENDENCY_INJECTION.md index 090f906e..7c34a574 100644 --- a/docs/contracts/DEPENDENCY_INJECTION.md +++ b/docs/contracts/DEPENDENCY_INJECTION.md @@ -22,6 +22,7 @@ references. No singletons, no service locators, no FastAPI Depends. _graph_driver: GraphDriver | None = None _domain_loader: DomainPackLoader | None = None + def init_dependencies(graph_driver: GraphDriver, domain_loader: DomainPackLoader) -> None: global _graph_driver, _domain_loader _graph_driver = graph_driver @@ -52,6 +53,7 @@ async def lifespan(app): async def handle_match(tenant, payload): driver = GraphDriver(...) # BANNED — creates new connection per request + # ❌ No importing settings directly in engine modules from chassis.settings import get_settings # BANNED — chassis concern diff --git a/docs/contracts/ERROR_HANDLING.md b/docs/contracts/ERROR_HANDLING.md index e40361b8..e58b6084 100644 --- a/docs/contracts/ERROR_HANDLING.md +++ b/docs/contracts/ERROR_HANDLING.md @@ -58,6 +58,7 @@ def compile_gate(self, gate: GateSpec) -> str | None: if gate.type not in SUPPORTED_TYPES: return None # silent skip + # ✅ CORRECT def compile_gate(self, gate: GateSpec) -> str: if gate.type not in SUPPORTED_TYPES: @@ -70,11 +71,14 @@ def compile_gate(self, gate: GateSpec) -> str: All error logs must include: tenant, action, trace_id (if available). ```python -logger.error(f"Gate compilation failed", extra={ - "tenant": tenant, - "gate_type": gate.type, - "match_direction": match_direction, -}) +logger.error( + f"Gate compilation failed", + extra={ + "tenant": tenant, + "gate_type": gate.type, + "match_direction": match_direction, + }, +) ``` ``` diff --git a/docs/contracts/FIELD_NAMES.md b/docs/contracts/FIELD_NAMES.md index 5ec71670..467df9b1 100644 --- a/docs/contracts/FIELD_NAMES.md +++ b/docs/contracts/FIELD_NAMES.md @@ -24,26 +24,26 @@ These are the EXACT attribute names. Use these and ONLY these. ### DomainSpec (top-level) ```python -domain_spec.domain # DomainMeta -domain_spec.domain.id # str — also the Neo4j database name -domain_spec.domain.name # str -domain_spec.domain.version # str -domain_spec.ontology # OntologySpec -domain_spec.ontology.nodes # list[NodeSpec] -domain_spec.ontology.edges # list[EdgeSpec] -domain_spec.match_entities # MatchEntitiesSpec +domain_spec.domain # DomainMeta +domain_spec.domain.id # str — also the Neo4j database name +domain_spec.domain.name # str +domain_spec.domain.version # str +domain_spec.ontology # OntologySpec +domain_spec.ontology.nodes # list[NodeSpec] +domain_spec.ontology.edges # list[EdgeSpec] +domain_spec.match_entities # MatchEntitiesSpec domain_spec.match_entities.candidate # list[CandidateEntity] -domain_spec.match_entities.query # list[QueryEntity] -domain_spec.traversal # TraversalSpec -domain_spec.traversal.steps # list[TraversalStep] -domain_spec.gates # list[GateSpec] -domain_spec.scoring # ScoringSpec -domain_spec.scoring.dimensions # list[ScoringDimension] -domain_spec.scoring.aggregation # str — "additive" or "multiplicative" -domain_spec.sync # SyncSpec -domain_spec.sync.endpoints # list[SyncEndpoint] -domain_spec.gds_jobs # list[GDSJobSpec] -domain_spec.compliance # ComplianceSpec +domain_spec.match_entities.query # list[QueryEntity] +domain_spec.traversal # TraversalSpec +domain_spec.traversal.steps # list[TraversalStep] +domain_spec.gates # list[GateSpec] +domain_spec.scoring # ScoringSpec +domain_spec.scoring.dimensions # list[ScoringDimension] +domain_spec.scoring.aggregation # str — "additive" or "multiplicative" +domain_spec.sync # SyncSpec +domain_spec.sync.endpoints # list[SyncEndpoint] +domain_spec.gds_jobs # list[GDSJobSpec] +domain_spec.compliance # ComplianceSpec domain_spec.compliance.prohibited_factors # list[str] ``` @@ -51,44 +51,44 @@ domain_spec.compliance.prohibited_factors # list[str] ### GateSpec ```python -gate.type # GateType enum value -gate.field # str — property name on candidate node -gate.query_param # str — key in query dict -gate.match_direction # str — which direction this gate applies to -gate.null_behavior # str — "pass" or "fail" -gate.params # dict[str, Any] — gate-type-specific parameters +gate.type # GateType enum value +gate.field # str — property name on candidate node +gate.query_param # str — key in query dict +gate.match_direction # str — which direction this gate applies to +gate.null_behavior # str — "pass" or "fail" +gate.params # dict[str, Any] — gate-type-specific parameters ``` ### ScoringDimension ```python -dim.type # ScoringType enum value -dim.weight # float -dim.field # str — property name on candidate node -dim.query_param # str — key in query dict -dim.params # dict[str, Any] — scoring-type-specific parameters +dim.type # ScoringType enum value +dim.weight # float +dim.field # str — property name on candidate node +dim.query_param # str — key in query dict +dim.params # dict[str, Any] — scoring-type-specific parameters ``` ### SyncEndpoint ```python -endpoint.path # str — e.g., "facilities" -endpoint.target_node # str — Neo4j label (MUST sanitize before Cypher) -endpoint.id_property # str — unique key property -endpoint.strategy # str — "merge" or "match_set" -endpoint.taxonomy_edges # list[TaxonomyEdge] -endpoint.children # list[ChildSync] +endpoint.path # str — e.g., "facilities" +endpoint.target_node # str — Neo4j label (MUST sanitize before Cypher) +endpoint.id_property # str — unique key property +endpoint.strategy # str — "merge" or "match_set" +endpoint.taxonomy_edges # list[TaxonomyEdge] +endpoint.children # list[ChildSync] ``` ### TraversalStep ```python -step.alias # str — Cypher variable alias -step.pattern # str — MATCH or OPTIONAL MATCH pattern -step.match_directions # list[str] — which directions include this step +step.alias # str — Cypher variable alias +step.pattern # str — MATCH or OPTIONAL MATCH pattern +step.match_directions # list[str] — which directions include this step ``` @@ -96,15 +96,15 @@ step.match_directions # list[str] — which directions include thi ```python # ❌ These DO NOT EXIST — agents must never generate these -domain_spec.matchentities # WRONG: missing underscore +domain_spec.matchentities # WRONG: missing underscore domain_spec.match_entities.candidates # WRONG: not plural 's'... wait yes it is -domain_spec.nodelabels # WRONG: not a field -domain_spec.matchdirections # WRONG: not a top-level field -gate.candidateprop # WRONG: field is called 'field' -gate.null_semantics # WRONG: field is called 'null_behavior' -dim.computation_type # WRONG: field is called 'type' -endpoint.targetnode # WRONG: missing underscore -endpoint.idproperty # WRONG: missing underscore +domain_spec.nodelabels # WRONG: not a field +domain_spec.matchdirections # WRONG: not a top-level field +gate.candidateprop # WRONG: field is called 'field' +gate.null_semantics # WRONG: field is called 'null_behavior' +dim.computation_type # WRONG: field is called 'type' +endpoint.targetnode # WRONG: missing underscore +endpoint.idproperty # WRONG: missing underscore ``` diff --git a/docs/contracts/HANDLER_PAYLOADS.md b/docs/contracts/HANDLER_PAYLOADS.md index f27de324..2b6b7915 100644 --- a/docs/contracts/HANDLER_PAYLOADS.md +++ b/docs/contracts/HANDLER_PAYLOADS.md @@ -20,11 +20,12 @@ incoming payloads against these schemas before processing. > TASK-040 / ADR-106: prefer `engine.models.payloads.MatchRequest` / `MatchResponse` (payload-only; no transport fields; no ungoverned weights). ```python class MatchPayload(BaseModel): - query: dict[str, Any] # Entity attributes to match against - match_direction: str # e.g., "buyer_to_seller" - top_n: int = 10 # Max candidates to return (1-1000) - weights: dict[str, float] = {} # Override scoring dimension weights - filters: dict[str, Any] = {} # Additional Cypher filters + query: dict[str, Any] # Entity attributes to match against + match_direction: str # e.g., "buyer_to_seller" + top_n: int = 10 # Max candidates to return (1-1000) + weights: dict[str, float] = {} # Override scoring dimension weights + filters: dict[str, Any] = {} # Additional Cypher filters + class MatchResponse(BaseModel): candidates: list[dict[str, Any]] @@ -39,8 +40,9 @@ class MatchResponse(BaseModel): ```python class SyncPayload(BaseModel): - entity_type: str # Must match a sync endpoint path - batch: list[dict[str, Any]] # 1-10000 entities per batch + entity_type: str # Must match a sync endpoint path + batch: list[dict[str, Any]] # 1-10000 entities per batch + class SyncResponse(BaseModel): status: Literal["success"] @@ -54,8 +56,9 @@ class SyncResponse(BaseModel): ```python class AdminPayload(BaseModel): subaction: Literal["list_domains", "get_domain", "init_schema", "trigger_gds"] - domain_id: str | None = None # Required for get_domain, init_schema, trigger_gds - job_name: str | None = None # Required for trigger_gds + domain_id: str | None = None # Required for get_domain, init_schema, trigger_gds + job_name: str | None = None # Required for trigger_gds + class AdminResponse(BaseModel): # Varies by subaction — always a dict diff --git a/docs/contracts/KGE_EMBEDDINGS.md b/docs/contracts/KGE_EMBEDDINGS.md index 7645583f..6076457a 100644 --- a/docs/contracts/KGE_EMBEDDINGS.md +++ b/docs/contracts/KGE_EMBEDDINGS.md @@ -69,7 +69,7 @@ belong in the single scoring clause. ```python # WRONG — cross-tenant vector reuse -index = shared_vector_index # violates tenant isolation +index = shared_vector_index # violates tenant isolation ``` ## Verified by diff --git a/docs/contracts/MEMORY_SUBSTRATE_ACCESS.md b/docs/contracts/MEMORY_SUBSTRATE_ACCESS.md index d46c1491..ebfa2ca6 100644 --- a/docs/contracts/MEMORY_SUBSTRATE_ACCESS.md +++ b/docs/contracts/MEMORY_SUBSTRATE_ACCESS.md @@ -37,11 +37,13 @@ When delegating to the constellation memory substrate node, the ingestion contra `ingest_packet()` on that node — reached via the delegation protocol, not by importing it: ```python -await ingest_packet(PacketEnvelopeIn( - packet_type="enrichment_result", - payload={"entity_id": "abc-123", "enriched_fields": {...}}, - tenant=TenantContext(actor="enrichment-engine", org_id="acme"), -)) +await ingest_packet( + PacketEnvelopeIn( + packet_type="enrichment_result", + payload={"entity_id": "abc-123", "enriched_fields": {...}}, + tenant=TenantContext(actor="enrichment-engine", org_id="acme"), + ) +) ``` diff --git a/docs/contracts/NODE_REGISTRATION.md b/docs/contracts/NODE_REGISTRATION.md index 9227da01..a207115c 100644 --- a/docs/contracts/NODE_REGISTRATION.md +++ b/docs/contracts/NODE_REGISTRATION.md @@ -58,9 +58,10 @@ node: ## BANNED ```python -"ScoreEngine" # WRONG → "score-engine" (lowercase, hyphenated) -"score_engine" # WRONG → "score-engine" (hyphens, not underscores) -"SCORE" # WRONG → "score-engine" (full name, not abbreviation) +"ScoreEngine" # WRONG → "score-engine" (lowercase, hyphenated) + +"score_engine" # WRONG → "score-engine" (hyphens, not underscores) +"SCORE" # WRONG → "score-engine" (full name, not abbreviation) ``` ``` diff --git a/docs/contracts/OBSERVABILITY.md b/docs/contracts/OBSERVABILITY.md index 171d4e29..c259818b 100644 --- a/docs/contracts/OBSERVABILITY.md +++ b/docs/contracts/OBSERVABILITY.md @@ -46,24 +46,29 @@ equally acceptable. Match the surrounding module; do not convert existing files. ```python import logging + logger = logging.getLogger(__name__) # ✅ CORRECT — stdlib logger, chassis owns handlers and formatting -logger.info("Gate compilation complete", extra={ - "gate_count": 10, - "match_direction": "buyer_to_seller", -}) +logger.info( + "Gate compilation complete", + extra={ + "gate_count": 10, + "match_direction": "buyer_to_seller", + }, +) # ✅ ALSO CORRECT — structlog getter, still zero configuration import structlog + logger = structlog.get_logger(__name__) logger.info("gate_compilation_complete", gate_count=10) # ❌ WRONG — configuring logging in engine -structlog.configure(...) # BANNED (OBS-001) — chassis does this +structlog.configure(...) # BANNED (OBS-001) — chassis does this # ❌ WRONG — creating custom formatters -logging.basicConfig(format="...") # BANNED (OBS-002) — chassis does this +logging.basicConfig(format="...") # BANNED (OBS-002) — chassis does this ``` > **Note:** no `structlog.configure()` call exists in `chassis/` in this repo either. Until the diff --git a/docs/contracts/PACKET_ENVELOPE_FIELDS.md b/docs/contracts/PACKET_ENVELOPE_FIELDS.md index 833fbb53..9e6e2d67 100644 --- a/docs/contracts/PACKET_ENVELOPE_FIELDS.md +++ b/docs/contracts/PACKET_ENVELOPE_FIELDS.md @@ -30,10 +30,10 @@ packet = build_request_packet( ) # Access fields -packet.header.action # "graph-query" -packet.header.trace_id # "trace-12345" -packet.payload # {"cypher": "..."} -packet.tenant.actor # "plasticos" +packet.header.action # "graph-query" +packet.header.trace_id # "trace-12345" +packet.payload # {"cypher": "..."} +packet.tenant.actor # "plasticos" ``` --- @@ -48,27 +48,28 @@ reads, or derives a packet MUST use these exact field names. No aliases. No abbr ```python class PacketEnvelope(BaseModel, frozen=True): - packet_id: UUID # Auto-generated, globally unique - packet_type: str # From PACKET_TYPE_REGISTRY.md - payload: dict[str, Any] # Domain-specific data — the ONLY field that varies - timestamp: datetime # UTC, auto-generated + packet_id: UUID # Auto-generated, globally unique + packet_type: str # From PACKET_TYPE_REGISTRY.md + payload: dict[str, Any] # Domain-specific data — the ONLY field that varies + timestamp: datetime # UTC, auto-generated ``` ## Standard Optional Fields ```python - metadata: PacketMetadata | None # schema_version, agent, domain +class PacketEnvelope(BaseModel, frozen=True): # continued: optional fields + metadata: PacketMetadata | None # schema_version, agent, domain provenance: PacketProvenance | None # source, tool, derive_type confidence: PacketConfidence | None # score (0.0-1.0), rationale - reasoning_block: dict | None # StructuredReasoningBlock if applicable - thread_id: UUID | None # Conversation/task grouping - lineage: PacketLineage | None # parent_ids, derivation_type, generation - tags: list[str] # Lightweight labels - ttl: datetime | None # Expiration for garbage collection - trace_id: str | None # W3C Trace Context ID - correlation_id: str | None # Cross-service correlation - content_hash: str # SHA-256 — auto-computed, UNIQUE constraint in DB + reasoning_block: dict | None # StructuredReasoningBlock if applicable + thread_id: UUID | None # Conversation/task grouping + lineage: PacketLineage | None # parent_ids, derivation_type, generation + tags: list[str] # Lightweight labels + ttl: datetime | None # Expiration for garbage collection + trace_id: str | None # W3C Trace Context ID + correlation_id: str | None # Cross-service correlation + content_hash: str # SHA-256 — auto-computed, UNIQUE constraint in DB ``` @@ -78,9 +79,9 @@ class PacketEnvelope(BaseModel, frozen=True): ```python class PacketMetadata(BaseModel, frozen=True): - schema_version: str # e.g., "1.1.0" - agent: str | None # Which agent/service created this - domain: str | None # e.g., "plasticos" + schema_version: str # e.g., "1.1.0" + agent: str | None # Which agent/service created this + domain: str | None # e.g., "plasticos" ``` @@ -88,9 +89,9 @@ class PacketMetadata(BaseModel, frozen=True): ```python class PacketProvenance(BaseModel, frozen=True): - source: str # e.g., "enrichment-engine", "graph-engine" - tool: str | None # e.g., "sonar-variations", "gate-compiler" - derive_type: str | None # e.g., "enrichment", "inference", "match" + source: str # e.g., "enrichment-engine", "graph-engine" + tool: str | None # e.g., "sonar-variations", "gate-compiler" + derive_type: str | None # e.g., "enrichment", "inference", "match" ``` @@ -98,8 +99,8 @@ class PacketProvenance(BaseModel, frozen=True): ```python class PacketConfidence(BaseModel, frozen=True): - score: float # 0.0 to 1.0 - rationale: str | None # Why this confidence level + score: float # 0.0 to 1.0 + rationale: str | None # Why this confidence level ``` @@ -107,9 +108,9 @@ class PacketConfidence(BaseModel, frozen=True): ```python class PacketLineage(BaseModel, frozen=True): - parent_ids: list[UUID] # Packets this was derived from - derivation_type: str # "enrichment", "inference", "match", "delegation" - generation: int # 0 = root, increments per derivation + parent_ids: list[UUID] # Packets this was derived from + derivation_type: str # "enrichment", "inference", "match", "delegation" + generation: int # 0 = root, increments per derivation ``` @@ -117,9 +118,9 @@ class PacketLineage(BaseModel, frozen=True): ```python class PacketAddress(BaseModel, frozen=True): - source_node: str # e.g., "plasticos" - destination_node: str # e.g., "enrichment-engine" - reply_to: str | None # Where to send the response + source_node: str # e.g., "plasticos" + destination_node: str # e.g., "enrichment-engine" + reply_to: str | None # Where to send the response ``` @@ -127,11 +128,11 @@ class PacketAddress(BaseModel, frozen=True): ```python class TenantContext(BaseModel, frozen=True): - actor: str # Who is doing it - on_behalf_of: str | None # Who authorized it - originator: str | None # Who started the chain - org_id: str # Tenant isolation key - user_id: str | None # Individual actor + actor: str # Who is doing it + on_behalf_of: str | None # Who authorized it + originator: str | None # Who started the chain + org_id: str # Tenant isolation key + user_id: str | None # Individual actor ``` @@ -156,13 +157,17 @@ original.payload["new_field"] = "value" # FROZEN — will crash ```python # Deterministic: sorted keys, canonical JSON import hashlib, json -hash_input = json.dumps({ - "packet_type": envelope.packet_type, - "action": envelope.payload.get("action"), - "payload": envelope.payload, - "tenant": envelope.tenant.actor, - "address": envelope.address.dict() if envelope.address else None, -}, sort_keys=True) + +hash_input = json.dumps( + { + "packet_type": envelope.packet_type, + "action": envelope.payload.get("action"), + "payload": envelope.payload, + "tenant": envelope.tenant.actor, + "address": envelope.address.dict() if envelope.address else None, + }, + sort_keys=True, +) content_hash = hashlib.sha256(hash_input.encode()).hexdigest() ``` @@ -170,14 +175,14 @@ content_hash = hashlib.sha256(hash_input.encode()).hexdigest() ## WRONG Field Names (agents generate these — they're all wrong) ```python -packetid # WRONG → packet_id -packettype # WRONG → packet_type -contentHash # WRONG → content_hash -threadId # WRONG → thread_id -traceId # WRONG → trace_id -parentIds # WRONG → parent_ids (inside PacketLineage) -sourceNode # WRONG → source_node (inside PacketAddress) -onBehalfOf # WRONG → on_behalf_of (inside TenantContext) +packetid # WRONG → packet_id +packettype # WRONG → packet_type +contentHash # WRONG → content_hash +threadId # WRONG → thread_id +traceId # WRONG → trace_id +parentIds # WRONG → parent_ids (inside PacketLineage) +sourceNode # WRONG → source_node (inside PacketAddress) +onBehalfOf # WRONG → on_behalf_of (inside TenantContext) ``` ``` diff --git a/docs/contracts/PII_HANDLING.md b/docs/contracts/PII_HANDLING.md index 3942b803..718dddd7 100644 --- a/docs/contracts/PII_HANDLING.md +++ b/docs/contracts/PII_HANDLING.md @@ -63,7 +63,7 @@ logger.info("pii_field_hashed", field="contact_email", handling=spec.compliance. ```python logger.info("processing contact", email=candidate["contact_email"]) # WRONG → PII in logs -key = "s3cr3t-aes-key" # WRONG → hardcoded key +key = "s3cr3t-aes-key" # WRONG → hardcoded key ``` ## Key sources diff --git a/docs/contracts/PYDANTIC_YAML_MAPPING.md b/docs/contracts/PYDANTIC_YAML_MAPPING.md index 7132f350..01e1499a 100644 --- a/docs/contracts/PYDANTIC_YAML_MAPPING.md +++ b/docs/contracts/PYDANTIC_YAML_MAPPING.md @@ -40,14 +40,15 @@ gates: # Python (engine/config/schema.py) class DomainSpec(BaseModel): domain: DomainMeta - match_entities: MatchEntitiesSpec # ← SAME as YAML key + match_entities: MatchEntitiesSpec # ← SAME as YAML key gates: list[GateSpec] + class GateSpec(BaseModel): type: GateType field: str query_param: str - null_behavior: str = "fail" # ← SAME as YAML key + null_behavior: str = "fail" # ← SAME as YAML key ``` @@ -58,10 +59,12 @@ class GateSpec(BaseModel): class GateSpec(BaseModel): null_behavior: str = Field(alias="nullBehavior") # BANNED + # ❌ No flatcase class DomainSpec(BaseModel): matchentities: MatchEntitiesSpec # BANNED — must be match_entities + # ❌ No camelCase class DomainSpec(BaseModel): matchEntities: MatchEntitiesSpec # BANNED diff --git a/docs/contracts/RETURN_VALUES.md b/docs/contracts/RETURN_VALUES.md index da8072d1..ecae18ef 100644 --- a/docs/contracts/RETURN_VALUES.md +++ b/docs/contracts/RETURN_VALUES.md @@ -36,6 +36,7 @@ async def handle_match(tenant: str, payload: dict) -> dict: if not payload.get("query"): raise ValueError("Missing required field: query") + # ❌ WRONG — returning error dict async def handle_match(tenant: str, payload: dict) -> dict: if not payload.get("query"): @@ -48,16 +49,16 @@ async def handle_match(tenant: str, payload: dict) -> dict: ```python # The chassis produces: { - "status": "success", # or "failed" + "status": "success", # or "failed" "action": "match", "tenant": "plasticos", - "data": { ... }, # ← THIS is what the engine returns + "data": {...}, # ← THIS is what the engine returns "meta": { "trace_id": "abc-123", "execution_ms": 45.2, "version": "1.1.0", "timestamp": "2026-03-01T20:00:00Z", - } + }, } ``` diff --git a/docs/contracts/SCORING_WEIGHT_CEILING.md b/docs/contracts/SCORING_WEIGHT_CEILING.md index 3885086e..f93c4e33 100644 --- a/docs/contracts/SCORING_WEIGHT_CEILING.md +++ b/docs/contracts/SCORING_WEIGHT_CEILING.md @@ -41,6 +41,7 @@ causal, persona) without breaching the ceiling. ```python _WEIGHT_CEILING = 1.0 + def _assert_default_weight_sum() -> None: weight_sum = settings.w_structural + settings.w_geo + settings.w_reinforcement + settings.w_freshness if weight_sum > _WEIGHT_CEILING + _WEIGHT_SUM_TOLERANCE: diff --git a/docs/contracts/SHARED_MODELS.md b/docs/contracts/SHARED_MODELS.md index bbb1904d..f24a3c70 100644 --- a/docs/contracts/SHARED_MODELS.md +++ b/docs/contracts/SHARED_MODELS.md @@ -71,15 +71,18 @@ types.py \# PacketType enum, shared type aliases from engine.packet.packet_envelope import PacketEnvelope, PacketLineage, PacketType, TenantContext from engine.packet.chassis_contract import deflate_egress, delegate_to_node, inflate_ingress + # ❌ WRONG — redefining in engine code class PacketEnvelope(BaseModel): # BANNED — already in l9-core packet_id: UUID ... + # ❌ WRONG — redefining TransportPacket class TransportPacket(BaseModel): # BANNED — already in constellation_node_sdk ... + # ❌ WRONG — copying the model file into your repo # cp ../enrichment-engine/models/envelope.py engine/models/ ``` diff --git a/docs/contracts/TEST_PATTERNS.md b/docs/contracts/TEST_PATTERNS.md index e0decb99..dcdd4fdd 100644 --- a/docs/contracts/TEST_PATTERNS.md +++ b/docs/contracts/TEST_PATTERNS.md @@ -20,13 +20,16 @@ the exact constructor signature. Do NOT guess. import pytest from engine.config.schema import DomainSpec, GateSpec, GateType + @pytest.fixture def sample_spec() -> DomainSpec: """Load a real domain spec — do NOT construct manually.""" from engine.config.loader import DomainPackLoader + loader = DomainPackLoader(domains_dir=Path("domains")) return loader.load_domain("plasticos") + def test_range_gate_compilation(sample_spec: DomainSpec): compiler = GateCompiler(sample_spec) # Match METHOD_SIGNATURES.md result = compiler.compile_gate(sample_spec.gates, "buyer_to_seller") @@ -41,11 +44,13 @@ def test_range_gate_compilation(sample_spec: DomainSpec): import pytest from testcontainers.neo4j import Neo4jContainer + @pytest.fixture(scope="module") def neo4j(): with Neo4jContainer("neo4j:5-enterprise") as container: yield container + # NEVER mock GraphDriver for integration tests # ALWAYS use testcontainers-neo4j ``` diff --git a/engine/compliance/audit.py b/engine/compliance/audit.py index cac0a24f..d22f6ed1 100644 --- a/engine/compliance/audit.py +++ b/engine/compliance/audit.py @@ -144,6 +144,7 @@ def log_access( actor: str, tenant: str, resource: str, + *, resource_type: str | None = None, trace_id: str | None = None, compliance_tags: list[str] | None = None, @@ -175,6 +176,7 @@ def log_mutation( tenant: str, resource: str, detail: str, + *, resource_type: str | None = None, trace_id: str | None = None, payload_hash: str | None = None, @@ -203,6 +205,7 @@ def log_query( actor: str, tenant: str, detail: str, + *, trace_id: str | None = None, compliance_tags: list[str] | None = None, metadata: dict[str, Any] | None = None, @@ -227,6 +230,7 @@ def log_pii_erasure( tenant: str, data_subject_id: str, detail: str, + *, trace_id: str | None = None, metadata: dict[str, Any] | None = None, ) -> AuditEntry: @@ -251,6 +255,7 @@ def log_delegation( tenant: str, resource: str, detail: str, + *, trace_id: str | None = None, compliance_tags: list[str] | None = None, metadata: dict[str, Any] | None = None, diff --git a/engine/health/enrichment_trigger.py b/engine/health/enrichment_trigger.py index 9c795f26..caf80379 100644 --- a/engine/health/enrichment_trigger.py +++ b/engine/health/enrichment_trigger.py @@ -150,6 +150,7 @@ def measure_health_impact( domain: str, health_before: EntityHealth, health_after: EntityHealth, + *, enrichment_cost_usd: float = 0.0, enrichment_tokens: int = 0, match_outcomes_before: list[Any] | None = None, diff --git a/engine/health/health_report.py b/engine/health/health_report.py index cdcde7c3..27da67c3 100644 --- a/engine/health/health_report.py +++ b/engine/health/health_report.py @@ -148,6 +148,7 @@ def track_conversion_event( tenant: str, entity_id: str, event_type: str, + *, tier_from: str = "seed", tier_to: str | None = None, metadata: dict[str, Any] | None = None, diff --git a/engine/intake/impact_reporter.py b/engine/intake/impact_reporter.py index 40f81274..01dd8806 100644 --- a/engine/intake/impact_reporter.py +++ b/engine/intake/impact_reporter.py @@ -268,16 +268,22 @@ def analyse_impact( def format_impact_summary(impact: ImpactAnalysis) -> str: """Format impact analysis as human-readable summary.""" lines = [ - f"YOUR CRM TODAY: {impact.current_field_count} fields, " - f"{impact.coverage_before:.0f}% coverage, " - f"AI-readiness {impact.ai_readiness_before}/10, " - f"{impact.gates_passable_before}/{impact.total_gates} gates", - f"AFTER ENRICH: {impact.coverage_after_enrich:.0f}% coverage, " - f"{impact.ai_readiness_after_enrich}/10, " - f"{impact.gates_passable_after}/{impact.total_gates} gates", - f"WITH DISCOVER: {impact.coverage_after_discover:.0f}% coverage, " - f"{impact.ai_readiness_after_discover}/10, " - f"{impact.total_gates}/{impact.total_gates} gates", + ( + f"YOUR CRM TODAY: {impact.current_field_count} fields, " + f"{impact.coverage_before:.0f}% coverage, " + f"AI-readiness {impact.ai_readiness_before}/10, " + f"{impact.gates_passable_before}/{impact.total_gates} gates" + ), + ( + f"AFTER ENRICH: {impact.coverage_after_enrich:.0f}% coverage, " + f"{impact.ai_readiness_after_enrich}/10, " + f"{impact.gates_passable_after}/{impact.total_gates} gates" + ), + ( + f"WITH DISCOVER: {impact.coverage_after_discover:.0f}% coverage, " + f"{impact.ai_readiness_after_discover}/10, " + f"{impact.total_gates}/{impact.total_gates} gates" + ), ] if impact.coverage_before > 0: improvement = impact.coverage_after_discover / impact.coverage_before diff --git a/engine/traversal/multihop.py b/engine/traversal/multihop.py index 9ef6c325..378ac482 100644 --- a/engine/traversal/multihop.py +++ b/engine/traversal/multihop.py @@ -162,6 +162,7 @@ class MultiHopTraverser: def __init__( self, neighbor_fetcher: NeighborFetcher, + *, reasoning_mode: ReasoningMode = ReasoningMode.SIMILARITY, max_hops: int = 4, top_k: int = 12, @@ -297,6 +298,7 @@ async def traverse( async def _execute_hop( self, + *, hop: int, queue: deque[str], llm_calls: int, diff --git a/poetry.lock b/poetry.lock index 4fb45331..6b84b55a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2061,30 +2061,30 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<8)"] [[package]] name = "ruff" -version = "0.16.7" +version = "0.16.8" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["dev"] files = [ - {file = "ruff-0.16.7-py3-none-linux_armv6l.whl", hash = "sha256:727307773e7c7f9181d3ed3a2484186e56c1fa1874255911c74585eb2c7c19f9"}, - {file = "ruff-0.16.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9d61c258deabf58f34c67bd4bb4d939c7f2e6b5f0e59c1cdd1cf771b11cde929"}, - {file = "ruff-0.16.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7ab81118df8945e0193d0240712aa4496573595b75185c3636ed825592a0f728"}, - {file = "ruff-0.16.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c196c968874fc8019da8e7163de7a1a370f111e2309b4b7dfea0fce950198d0"}, - {file = "ruff-0.16.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac8c3bd0a7e10ad31e6ce51e7a99f3cb772e69aecdd6b9ea7e99b362f62a62c0"}, - {file = "ruff-0.16.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:398d3988edde000b5c75dc1b3f584708da9bc990de069c18909142580fec1af9"}, - {file = "ruff-0.16.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce05b62b770a8217c4646a9c4139fca00efe8fe5d71f87df2b243ff20d4584d1"}, - {file = "ruff-0.16.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af1b576fddb9d9ef2ececfb5fadcd6a624b25070ed85e3cfcfe449fc3ff6a7b9"}, - {file = "ruff-0.16.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ce7f8f22df67c93ed96c717f9128eadb797144ac2bad475cf536f31d6100c55"}, - {file = "ruff-0.16.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:06d0e93d04f392996435ebd600c153f65b47d73fbec2415aa99c5ee5756b3a5f"}, - {file = "ruff-0.16.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:142151a5e7b93c1b11111337142f89dd2fbfee92161225c99a97222f22e32656"}, - {file = "ruff-0.16.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e6651f97a342d8b35d54d8991544ca22169b86dc54111cb604666940c431b750"}, - {file = "ruff-0.16.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ef140c6eb935fa9a84c9c607dfb2cb1b85843c192e79265b0c54f35f557ea8e5"}, - {file = "ruff-0.16.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:53e39506a730fadeee0d998ed5946f30671f0db240c6c7c73bdabbe33604bb6f"}, - {file = "ruff-0.16.7-py3-none-win32.whl", hash = "sha256:2ea3470fcebcbc5df2fb0c6f3b90333fa9084c534e0111c038fa4a6ab9f1c4b7"}, - {file = "ruff-0.16.7-py3-none-win_amd64.whl", hash = "sha256:7ac26aca826e9e21d0f1cb25b54ac660760a9fdd094d3e4df9848232be98cfc6"}, - {file = "ruff-0.16.7-py3-none-win_arm64.whl", hash = "sha256:aab7f39e2c9df6c596216070f98eef1207b94f8516cca20c808826974971855b"}, - {file = "ruff-0.16.7.tar.gz", hash = "sha256:5f71d004ac1263b22fa39462ac5ae618a4b77d58981af2cc79bf79a29c12b1a6"}, + {file = "ruff-0.16.8-py3-none-linux_armv6l.whl", hash = "sha256:6ffbd6d87383c1edf5f6fa890f10200950240d7c1a16052a19a09d3a2307dd38"}, + {file = "ruff-0.16.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:42ed6b878ed61e3acca92f2730a17acff39286944ea82398544696366a6f925e"}, + {file = "ruff-0.16.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7ea781c7f2afba8c6a505ea0fb3f994020249e0c450635f5381286fea6b46170"}, + {file = "ruff-0.16.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8efeae3bbe414a5efefda11a792dfb51ef90ac48d50c4830de2f644caf3e8659"}, + {file = "ruff-0.16.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a79b795469fef7fc6e908b218eed2eb17332afd85031db6480dc864560e69b2"}, + {file = "ruff-0.16.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fdc5563cdc50555e6fba39322850860e9267c1b3d12c26a74729d8604c3c812"}, + {file = "ruff-0.16.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34508983c70665578dab88f5223d8e6228307e1135398ca8bfc8b7e9501e282b"}, + {file = "ruff-0.16.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:644bb578569e0ffc575741232bd385dacdd6fbe123f1a729e7a225f54aa3957f"}, + {file = "ruff-0.16.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15e7d226246961db9235098333caa13063906d3851136b84c2900b82f5daa1df"}, + {file = "ruff-0.16.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a2bf6bc3e9ebdd4449abc6f06cf64b98051a2c61cf94d2fe9596518c881f1a1e"}, + {file = "ruff-0.16.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6ca111ba0849539165e9e59d2b442542f3c1e8060ebbdea82494f1ffbccb1e1f"}, + {file = "ruff-0.16.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:359a1e5b495448ee1e91018064382ebc86f90e8aac2fed222c7d0e4e8df85fd2"}, + {file = "ruff-0.16.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:59e8f5681349474110b24d62e93cfda6593f5fa3473446ca3705200cac1a08b9"}, + {file = "ruff-0.16.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:efa3e7a16d1baaa79957888dfdf8be9ef2e44db81cb032af06d76632ab59e773"}, + {file = "ruff-0.16.8-py3-none-win32.whl", hash = "sha256:55793ba85c69921e89be061426d91a78652d6e50317c962240922747a4eb713f"}, + {file = "ruff-0.16.8-py3-none-win_amd64.whl", hash = "sha256:a6b85621fd3c81e31fc5f5add09c9c078b430db3595ca632efafdec9e64ebfaa"}, + {file = "ruff-0.16.8-py3-none-win_arm64.whl", hash = "sha256:d075e820af612102ce217f07cc93e69f9490b10ec13ea85fa87bd03d996cef8a"}, + {file = "ruff-0.16.8.tar.gz", hash = "sha256:9247bf92b5f04d825c8639a4fe423ec2e4222acd9222e58412b0dab7e442798b"}, ] [[package]] @@ -2695,4 +2695,4 @@ dev = ["pytest", "setuptools"] [metadata] lock-version = "2.1" python-versions = "^3.12" -content-hash = "e431dd032d19b38b6c9a3dcf9f63ee1dfa56caa343bf98b56872e00c5be2bd4f" +content-hash = "d262a46bfbfcaed3011527a67abea0a42564d5b4e9a6d8fbef3434906d665d5d" diff --git a/pyproject.toml b/pyproject.toml index 1f86cb10..688b4f48 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ pytest = "9.1.1" pytest-asyncio = "^1.3.0" pytest-cov = "7.1.0" pytest-mock = "^3.14.0" -ruff = "0.16.7" +ruff = "0.16.8" mypy = "2.3.1" types-PyYAML = ">=6.0" faker = "^40.5.1" diff --git a/reports/hoprag_ceg_enhancement_program.md b/reports/hoprag_ceg_enhancement_program.md index a8d37b2b..4e8d7b17 100644 --- a/reports/hoprag_ceg_enhancement_program.md +++ b/reports/hoprag_ceg_enhancement_program.md @@ -651,10 +651,7 @@ In `"similarity"` mode, the traverser replaces the LLM call with: ```python def _select_next_edge_similarity(self, query_embedding, candidate_edges): - best_edge = max( - candidate_edges, - key=lambda e: cosine_similarity(query_embedding, e.embedding) - ) + best_edge = max(candidate_edges, key=lambda e: cosine_similarity(query_embedding, e.embedding)) return best_edge ``` diff --git a/requirements-ci.txt b/requirements-ci.txt index 037339b3..9d602cac 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -6,8 +6,8 @@ # Dev: pip install -r requirements-ci.txt -r requirements-dev.txt # ── Lint & type checking ────────────────────────────────────────────────────── -ruff==0.15.12 -mypy==1.14.0 +ruff==0.16.8 +mypy==2.3.1 # ── Type stubs ──────────────────────────────────────────────────────────────── types-PyYAML==6.0.12.20250915 diff --git a/requirements-dev.txt b/requirements-dev.txt index eb13dcba..d0c50bd0 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -9,8 +9,8 @@ pytest-cov==7.1.0 pytest-mock>=3.14.0,<4.0.0 pytest-xdist>=3.8.0,<4.0.0 pytest-timeout>=2.4.0,<3.0.0 -ruff==0.15.12 -mypy==1.14.0 +ruff==0.16.8 +mypy==2.3.1 faker>=40.5.1,<41.0.0 jsonschema>=4.23.0,<5.0.0 hypothesis>=6.100,<7 # tests/property/* hard-import hypothesis; precommit pytest-unit hook collects them diff --git a/tests/README.md b/tests/README.md index d24ca438..db2c014e 100644 --- a/tests/README.md +++ b/tests/README.md @@ -128,19 +128,13 @@ docker run -d --name neo4j-test \ import pytest from engine.gates.types.threshold import ThresholdGate + def test_threshold_gate_lte_pass(): gate = ThresholdGate( - name="credit_min", - candidateprop="mincreditscore", - queryparam="creditscore", - operator="<=", - nullbehavior="fail" + name="credit_min", candidateprop="mincreditscore", queryparam="creditscore", operator="<=", nullbehavior="fail" ) - cypher = gate.compile( - candidate_alias="c", - query_alias="$query" - ) + cypher = gate.compile(candidate_alias="c", query_alias="$query") assert "c.mincreditscore <= $query.creditscore" in cypher ``` @@ -151,6 +145,7 @@ def test_threshold_gate_lte_pass(): import pytest from testcontainers.neo4j import Neo4jContainer + @pytest.mark.asyncio async def test_full_match_pipeline(neo4j_container, test_domain): # Setup test data @@ -165,16 +160,11 @@ async def test_full_match_pipeline(neo4j_container, test_domain): import pytest from engine.compliance.prohibited_factors import ProhibitedFactorValidator + def test_ecoa_blocks_race_in_gates(): - validator = ProhibitedFactorValidator( - regime="ECOA", - blocked_fields=["race", "ethnicity"] - ) + validator = ProhibitedFactorValidator(regime="ECOA", blocked_fields=["race", "ethnicity"]) - gate_config = { - "candidateprop": "race", - "queryparam": "race" - } + gate_config = {"candidateprop": "race", "queryparam": "race"} with pytest.raises(ProhibitedFactorError): validator.validate_gate(gate_config) diff --git a/tests/test_algorithmic_upgrades.py b/tests/test_algorithmic_upgrades.py index 243fc92b..e8c5f420 100644 --- a/tests/test_algorithmic_upgrades.py +++ b/tests/test_algorithmic_upgrades.py @@ -154,7 +154,7 @@ def test_symmetry(self): class TestDetectDrift: def _make_fp( - self, persona_id: str, window_id: str, score_dist: dict, dim_dom: dict, entropy: float, concentration: float + self, persona_id: str, window_id: str, score_dist: dict, dim_dom: dict, *, entropy: float, concentration: float ): return AlgorithmicFingerprint( persona_id=persona_id, @@ -168,36 +168,38 @@ def _make_fp( ) def test_no_drift_identical(self): - fp = self._make_fp("p1", "w1", {"low": 0.5, "high": 0.5}, {"geo": 0.6, "rev": 0.4}, 1.0, 0.5) + fp = self._make_fp( + "p1", "w1", {"low": 0.5, "high": 0.5}, {"geo": 0.6, "rev": 0.4}, entropy=1.0, concentration=0.5 + ) report = detect_drift(fp, fp) assert not report.drift_detected assert report.severity == "none" assert report.drift_reasons == [] def test_drift_score_shift(self): - baseline = self._make_fp("p1", "w1", {"low": 0.8, "high": 0.2}, {"geo": 0.5}, 0.7, 0.8) - current = self._make_fp("p1", "w2", {"low": 0.2, "high": 0.8}, {"geo": 0.5}, 0.7, 0.8) + baseline = self._make_fp("p1", "w1", {"low": 0.8, "high": 0.2}, {"geo": 0.5}, entropy=0.7, concentration=0.8) + current = self._make_fp("p1", "w2", {"low": 0.2, "high": 0.8}, {"geo": 0.5}, entropy=0.7, concentration=0.8) report = detect_drift(baseline, current, score_threshold=0.1) assert report.drift_detected assert any("Score distribution" in r for r in report.drift_reasons) def test_drift_entropy_change(self): - baseline = self._make_fp("p1", "w1", {"low": 0.5, "high": 0.5}, {"geo": 0.5}, 0.5, 0.5) - current = self._make_fp("p1", "w2", {"low": 0.5, "high": 0.5}, {"geo": 0.5}, 1.5, 0.5) + baseline = self._make_fp("p1", "w1", {"low": 0.5, "high": 0.5}, {"geo": 0.5}, entropy=0.5, concentration=0.5) + current = self._make_fp("p1", "w2", {"low": 0.5, "high": 0.5}, {"geo": 0.5}, entropy=1.5, concentration=0.5) report = detect_drift(baseline, current, entropy_threshold=0.3) assert report.drift_detected assert any("Entropy" in r for r in report.drift_reasons) def test_high_severity_multiple_reasons(self): - baseline = self._make_fp("p1", "w1", {"low": 0.9, "high": 0.1}, {"geo": 0.9}, 0.3, 0.9) - current = self._make_fp("p1", "w2", {"low": 0.1, "high": 0.9}, {"rev": 0.9}, 1.5, 0.1) + baseline = self._make_fp("p1", "w1", {"low": 0.9, "high": 0.1}, {"geo": 0.9}, entropy=0.3, concentration=0.9) + current = self._make_fp("p1", "w2", {"low": 0.1, "high": 0.9}, {"rev": 0.9}, entropy=1.5, concentration=0.1) report = detect_drift(baseline, current) assert report.drift_detected assert report.severity == "high" assert len(report.drift_reasons) >= 3 def test_to_dict(self): - baseline = self._make_fp("p1", "w1", {"low": 0.5}, {"geo": 0.5}, 1.0, 0.5) + baseline = self._make_fp("p1", "w1", {"low": 0.5}, {"geo": 0.5}, entropy=1.0, concentration=0.5) report = detect_drift(baseline, baseline) d = report.to_dict() assert isinstance(d, dict) diff --git a/tests/unit/test_gds_scheduler.py b/tests/unit/test_gds_scheduler.py index 66a1750a..f3b63a01 100644 --- a/tests/unit/test_gds_scheduler.py +++ b/tests/unit/test_gds_scheduler.py @@ -43,6 +43,7 @@ def _job( name: str, algorithm: str, + *, schedule_type: str = "cron", cron: str | None = "0 2 * * *", node_labels: list[str] | None = None, diff --git a/tests/unit/test_health_engine.py b/tests/unit/test_health_engine.py index d7e7944c..634a056c 100644 --- a/tests/unit/test_health_engine.py +++ b/tests/unit/test_health_engine.py @@ -83,6 +83,7 @@ def _make_domain_spec( def _make_field_health( name: str, + *, populated: bool = True, confidence: float | None = 0.9, gate_critical: bool = False, diff --git a/tests/unit/test_resolver.py b/tests/unit/test_resolver.py index e4f58e93..975cb843 100644 --- a/tests/unit/test_resolver.py +++ b/tests/unit/test_resolver.py @@ -24,6 +24,7 @@ def _make_spec( + *, threshold: float = 0.85, property_weight: float = 0.5, structural_weight: float = 0.3, diff --git a/tests/unit/test_signal_weights.py b/tests/unit/test_signal_weights.py index cd1a8fd8..7269e0d6 100644 --- a/tests/unit/test_signal_weights.py +++ b/tests/unit/test_signal_weights.py @@ -39,6 +39,7 @@ def _spec_with_dimensions( + *, dimensions: list[ScoringDimensionSpec] | None = None, min_weight: float = 0.1, max_weight: float = 3.0, diff --git a/tests/unit/test_sync.py b/tests/unit/test_sync.py index 4c2a3306..8650f522 100644 --- a/tests/unit/test_sync.py +++ b/tests/unit/test_sync.py @@ -28,6 +28,7 @@ def make_mock_domain_spec() -> MagicMock: def make_mock_sync_endpoint( + *, path: str = "/sync/test", strategy: SyncStrategy = SyncStrategy.UNWINDMERGE, target_node: str = "TestNode", diff --git a/tools/auditors/log_safety.py b/tools/auditors/log_safety.py index aec88aa0..82b97da0 100644 --- a/tools/auditors/log_safety.py +++ b/tools/auditors/log_safety.py @@ -92,6 +92,7 @@ def _classify_line(self, line: str) -> tuple[bool, bool] | None: def _emit_finding( self, + *, result: AuditResult, counter: list[int], tok: str, @@ -126,7 +127,7 @@ def _scan_sensitive_logs( ll = line.lower() for tok in SENSITIVE: if tok in ll: - self._emit_finding(result, counter, tok, is_log, rel, i) + self._emit_finding(result=result, counter=counter, tok=tok, is_log=is_log, rel=rel, lineno=i) break def _scan_trace_leaks( diff --git a/tools/contract_scanner.py b/tools/contract_scanner.py index 89b7f014..52a61143 100644 --- a/tools/contract_scanner.py +++ b/tools/contract_scanner.py @@ -47,8 +47,8 @@ def _rule( severity: str, pattern: str, message: str, - remediation: str, *, + remediation: str, include_dirs: list[str] | None = None, exclude_dirs: list[str] | None = None, ) -> dict: @@ -75,7 +75,7 @@ def _rule( "CRITICAL", r'f["\'].*MATCH\s*\(.*\{[^$]', "Cypher label interpolation without sanitize_label()", - "Use sanitize_label() for labels, $param for values", + remediation="Use sanitize_label() for labels, $param for values", include_dirs=[ENGINE_DIR], exclude_dirs=[ "engine/sync/generator.py", @@ -90,7 +90,7 @@ def _rule( "CRITICAL", r"\beval\s*\(", "eval() is banned - code injection risk", - "Use operator dispatch table or ast.literal_eval()", + remediation="Use operator dispatch table or ast.literal_eval()", exclude_dirs=["tests/", CONTRACT_SCANNER_PATH, "engine/utils/safe_eval.py"], # AST-based; no eval() ), _rule( @@ -99,7 +99,7 @@ def _rule( "CRITICAL", r"\bexec\s*\(", "exec() is banned - code injection risk", - "Remove entirely", + remediation="Remove entirely", exclude_dirs=["tests/", CONTRACT_SCANNER_PATH, "engine/security/"], ), _rule( @@ -108,7 +108,7 @@ def _rule( "CRITICAL", r'f["\'].*LIMIT\s*\{', "LIMIT value interpolation - use $limit parameter", - "LIMIT $limit with params={'limit': n}", + remediation="LIMIT $limit with params={'limit': n}", ), _rule( "SEC-005", @@ -116,7 +116,7 @@ def _rule( "CRITICAL", r'f["\'].*(?:SELECT|INSERT|UPDATE|DELETE)\s.*\{', "SQL string interpolation - use parameterized queries", - "Use $1/$2 placeholders or ORM", + remediation="Use $1/$2 placeholders or ORM", ), _rule( "SEC-006", @@ -124,7 +124,7 @@ def _rule( "CRITICAL", r"pickle\.loads?\s*\(", "pickle banned - deserialization attack vector", - "Use json.loads()", + remediation="Use json.loads()", ), _rule( "SEC-007", @@ -132,7 +132,7 @@ def _rule( "CRITICAL", r"yaml\.load\s*\([^)]*\)\s*$", "yaml.load() without SafeLoader", - "yaml.safe_load()", + remediation="yaml.safe_load()", ), # -- CONTRACT 4: ERROR_HANDLING.md -- _rule( @@ -141,7 +141,7 @@ def _rule( "HIGH", r"except\s*:", "Bare except: clause", - "except SpecificError as e:", + remediation="except SpecificError as e:", exclude_dirs=[CONTRACT_SCANNER_PATH], ), _rule( @@ -150,7 +150,7 @@ def _rule( "HIGH", r"except\s+\w+.*:\s*\n\s*pass", "Swallowed exception - except + pass", - "Log and re-raise", + remediation="Log and re-raise", ), # -- CONTRACT 10: BANNED_PATTERNS.md (Architecture) -- _rule( @@ -159,7 +159,7 @@ def _rule( "CRITICAL", r"from\s+fastapi\s+import", "FastAPI import in engine/ - chassis owns HTTP", - "Register handlers in engine/handlers.py", + remediation="Register handlers in engine/handlers.py", include_dirs=[ENGINE_DIR], ), _rule( @@ -168,7 +168,7 @@ def _rule( "CRITICAL", r"from\s+starlette\s+import", "Starlette import in engine/ - chassis owns middleware", - "Remove", + remediation="Remove", include_dirs=[ENGINE_DIR], ), _rule( @@ -177,7 +177,7 @@ def _rule( "CRITICAL", r"import\s+uvicorn", "uvicorn import in engine/ - chassis owns ASGI", - "Remove", + remediation="Remove", include_dirs=[ENGINE_DIR], ), # -- CONTRACT 7: DEPENDENCY_INJECTION.md -- @@ -187,7 +187,7 @@ def _rule( "HIGH", r"from\s+fastapi\s+import\s+Depends", "FastAPI Depends in engine/ - chassis concern", - "Use init_dependencies() pattern", + remediation="Use init_dependencies() pattern", include_dirs=[ENGINE_DIR], ), # -- CONTRACT 12: DELEGATION_PROTOCOL.md -- @@ -197,7 +197,7 @@ def _rule( "CRITICAL", r"httpx\.(post|get|put|delete|patch)\s*\(", "Raw HTTP to another node - use delegate_to_node()", - "from engine.packet.chassis_contract import delegate_to_node", + remediation="from engine.packet.chassis_contract import delegate_to_node", include_dirs=[ENGINE_DIR], ), _rule( @@ -206,7 +206,7 @@ def _rule( "CRITICAL", r"requests\.(post|get|put|delete|patch)\s*\(", "Raw HTTP via requests - use delegate_to_node()", - "from engine.packet.chassis_contract import delegate_to_node", + remediation="from engine.packet.chassis_contract import delegate_to_node", include_dirs=[ENGINE_DIR], ), # -- CONTRACT 19: MEMORY_SUBSTRATE_ACCESS.md -- @@ -216,7 +216,7 @@ def _rule( "CRITICAL", r"INSERT\s+INTO\s+packetstore", "Direct write to packetstore - use ingest_packet()", - "Persist via engine.packet.packet_store, or delegate to the memory substrate node", + remediation="Persist via engine.packet.packet_store, or delegate to the memory substrate node", include_dirs=[ENGINE_DIR], ), _rule( @@ -225,7 +225,7 @@ def _rule( "CRITICAL", r"INSERT\s+INTO\s+memory_embeddings", "Direct write to memory_embeddings - use ingest_packet()", - "Embeddings generated by LangGraph DAG", + remediation="Embeddings generated by LangGraph DAG", include_dirs=[ENGINE_DIR], ), # -- CONTRACT 20: SHARED_MODELS.md -- @@ -235,7 +235,7 @@ def _rule( "HIGH", r"class\s+TransportPacket\s*\(", "Redefining TransportPacket - import the shared model", - "from constellation_node_sdk import TransportPacket", + remediation="from constellation_node_sdk import TransportPacket", include_dirs=[ENGINE_DIR], exclude_dirs=["engine/packet/packet_envelope.py"], # canonical envelope in this repo ), @@ -245,7 +245,7 @@ def _rule( "HIGH", r"class\s+TenantContext\s*\(", "Redefining TenantContext - import the shared model", - "from engine.packet.packet_envelope import TenantContext", + remediation="from engine.packet.packet_envelope import TenantContext", include_dirs=[ENGINE_DIR], exclude_dirs=["engine/packet/packet_envelope.py"], # canonical envelope in this repo ), @@ -255,7 +255,7 @@ def _rule( "HIGH", r"class\s+ExecuteRequest\s*\(", "Redefining ExecuteRequest - the chassis owns this model", - "from chassis.chassis_app import ExecuteRequest", + remediation="from chassis.chassis_app import ExecuteRequest", include_dirs=[ENGINE_DIR], ), # -- CONTRACT 18: OBSERVABILITY.md -- @@ -265,7 +265,7 @@ def _rule( "HIGH", r"structlog\.configure\s*\(", "Configuring structlog in engine - chassis does this", - "Use logging.getLogger(__name__)", + remediation="Use logging.getLogger(__name__)", include_dirs=[ENGINE_DIR], ), _rule( @@ -274,7 +274,7 @@ def _rule( "HIGH", r"logging\.basicConfig\s*\(", "Configuring logging in engine - chassis does this", - "Use logging.getLogger(__name__) only", + remediation="Use logging.getLogger(__name__) only", include_dirs=[ENGINE_DIR], ), # -- CONTRACT 6: PYDANTIC_YAML_MAPPING.md -- @@ -284,7 +284,7 @@ def _rule( "HIGH", r"Field\s*\(\s*alias\s*=", "Pydantic Field alias banned - snake_case everywhere", - "Remove alias, use snake_case matching YAML key", + remediation="Remove alias, use snake_case matching YAML key", include_dirs=[ENGINE_DIR], ), # -- CONTRACT 13: PACKET_TYPE_REGISTRY.md -- @@ -294,7 +294,7 @@ def _rule( "HIGH", r'packet_type\s*[=:]\s*["\'][A-Z]', "Uppercase packet_type - must be lowercase snake_case", - "Check PACKET_TYPE_REGISTRY.md", + remediation="Check PACKET_TYPE_REGISTRY.md", exclude_dirs=["agents/cursor/"], ), # -- CONTRACT 17: zero-stub protocol (TEST_PATTERNS.md / BANNED_PATTERNS.md) -- @@ -306,7 +306,7 @@ def _rule( "CRITICAL", r"raise\s+NotImplementedError", "Stub in engine/ - unimplemented code path", - "Ship the implementation or record the gap in DEFERRED.md", + remediation="Ship the implementation or record the gap in DEFERRED.md", include_dirs=[ENGINE_DIR], ), _rule( @@ -315,7 +315,7 @@ def _rule( "HIGH", r"#\s*TODO\b", "TODO comment in engine/ - deferred work must be tracked, not inlined", - "Implement it now or add an entry to DEFERRED.md and drop the comment", + remediation="Implement it now or add an entry to DEFERRED.md and drop the comment", include_dirs=[ENGINE_DIR], ), _rule( @@ -324,7 +324,7 @@ def _rule( "HIGH", r"#\s*(?:PLACEHOLDER|FIXME|XXX)\b", "PLACEHOLDER/FIXME comment in engine/ - deferred work must be tracked, not inlined", - "Implement it now or add an entry to DEFERRED.md and drop the comment", + remediation="Implement it now or add an entry to DEFERRED.md and drop the comment", include_dirs=[ENGINE_DIR], ), # -- CONTRACT 05: ENV_VARS.md -- @@ -334,7 +334,7 @@ def _rule( "MEDIUM", r'os\.environ\[["\']?(?:NEO4J_URI|NEO4J_URL|DATABASE_URL|REDIS_HOST|API_KEY)["\']?\]', "Non-standard env var name", - "Use L9_* or ENGINE_* prefix per ENV_VARS.md", + remediation="Use L9_* or ENGINE_* prefix per ENV_VARS.md", ), ] diff --git a/tools/l9_meta_injector.py b/tools/l9_meta_injector.py index 990ebfe8..a0c97480 100644 --- a/tools/l9_meta_injector.py +++ b/tools/l9_meta_injector.py @@ -506,7 +506,7 @@ class FileMeta: FileMeta(".gitleaks.toml", "l9-template", ["security"], ["L9_TEMPLATE", "gitleaks", "secrets"], "platform"), # --- docs/ folder --- FileMeta("docs/ARCHITECTURE.md", "engine-specific", ["docs"], ["architecture", "design"], "engine-team"), - FileMeta("docs/ACTION ITEMS.MD", "engine-specific", ["docs"], ["action-items"], "engine-team"), + FileMeta("docs/ACTION ITEMS.md", "engine-specific", ["docs"], ["action-items"], "engine-team"), FileMeta("docs/Audit Harness-Explained.md", "engine-specific", ["docs"], ["audit", "harness"], "engine-team"), FileMeta("docs/What the Audit Harness Does.md", "engine-specific", ["docs"], ["audit", "harness"], "engine-team"), FileMeta("docs/GRAPH-architecture.md", "engine-specific", ["docs"], ["architecture", "graph"], "engine-team"), diff --git a/tools/research/cognitive-engine-revenue-patterns.md b/tools/research/cognitive-engine-revenue-patterns.md index 3dd106e7..2277250b 100644 --- a/tools/research/cognitive-engine-revenue-patterns.md +++ b/tools/research/cognitive-engine-revenue-patterns.md @@ -98,19 +98,12 @@ def collaborative_filtering_rank(query_entity, candidate_pool): """ # Step 1: Find historical successes for similar queries - similar_queries = graph.traverse( - start=query_entity, - relationship="SIMILAR_TO", - depth=1 - ) + similar_queries = graph.traverse(start=query_entity, relationship="SIMILAR_TO", depth=1) historical_successes = [] for similar_q in similar_queries: successes = graph.traverse( - start=similar_q, - relationship="HISTORICAL_SUCCESS", - filters={"outcome": "positive"}, - depth=1 + start=similar_q, relationship="HISTORICAL_SUCCESS", filters={"outcome": "positive"}, depth=1 ) historical_successes.extend(successes) @@ -120,24 +113,16 @@ def collaborative_filtering_rank(query_entity, candidate_pool): # How often did this candidate co-occur with # historically successful candidates? co_occurrences = graph.traverse( - start=candidate, - relationship="CO_OCCURRED_WITH", - targets=historical_successes, - depth=1 + start=candidate, relationship="CO_OCCURRED_WITH", targets=historical_successes, depth=1 ) # Score = frequency × lift × recency decay - score = sum( - edge.frequency * edge.lift * decay(edge.timestamp) - for edge in co_occurrences - ) + score = sum(edge.frequency * edge.lift * decay(edge.timestamp) for edge in co_occurrences) candidate_scores[candidate] = score # Step 3: Rank and return top-K - return sorted(candidate_scores.items(), - key=lambda x: x[1], - reverse=True)[:K] + return sorted(candidate_scores.items(), key=lambda x: x[1], reverse=True)[:K] ``` #### PlasticOS Revenue Impact Projection @@ -274,7 +259,7 @@ def disambiguate_via_context(query, context_signals): candidates = graph.traverse( start=query, relationship="COULD_BE", # Ambiguous mapping - depth=1 + depth=1, ) if len(candidates) == 1: @@ -287,10 +272,7 @@ def disambiguate_via_context(query, context_signals): # Traverse context → candidate compatibility for context in context_signals: - compatibility = graph.get_edge( - context, candidate, - relationship="SUPPORTS_ENTITY" - ) + compatibility = graph.get_edge(context, candidate, relationship="SUPPORTS_ENTITY") if compatibility: score += compatibility.strength * context.signal_strength @@ -388,15 +370,13 @@ LIMIT 1 # Step 1: Train embeddings from graph structure embeddings = graph_neural_network( - graph=knowledge_graph, - node_features=attributes, - edge_features=relationships, - embedding_dim=128 + graph=knowledge_graph, node_features=attributes, edge_features=relationships, embedding_dim=128 ) # Step 2: Index for fast similarity search index = build_vector_index(embeddings) # FAISS, Annoy, etc. + # Step 3: Query-time similarity def find_similar(query_entity, k=10): query_embedding = embeddings[query_entity] @@ -532,11 +512,7 @@ def real_time_rank(base_candidates, session_context): final_scores = {} for candidate in base_candidates: # Combine graph score + context - features = { - 'base_graph_score': base_scores[candidate.id], - **context_features, - **candidate.real_time_attributes - } + features = {"base_graph_score": base_scores[candidate.id], **context_features, **candidate.real_time_attributes} final_scores[candidate.id] = ranking_model.predict(features) @@ -773,6 +749,7 @@ collaborative_filtering: from datetime import datetime, timedelta from neo4j import AsyncGraphDatabase + async def build_co_occurrence_edges(tx, window_days: int = 90): """ Build CO_OCCURRED_WITH edges from transaction history @@ -822,15 +799,18 @@ async def build_co_occurrence_edges(tx, window_days: int = 90): from pydantic import BaseModel from typing import Optional + class RealTimeContext(BaseModel): urgency: Optional[str] = "normal" # "normal" | "high" | "urgent" current_capacity: Optional[dict] = None # Facility capacity overrides recent_performance: Optional[dict] = None # Last 7 days performance metrics + class MatchRequest(BaseModel): # ... existing fields ... real_time_context: Optional[RealTimeContext] = None + @router.post("/v1/match") async def match_with_context(request: MatchRequest): """ @@ -847,20 +827,13 @@ async def match_with_context(request: MatchRequest): # Step 2: Real-time re-ranking context_features = { - 'urgency_multiplier': { - 'normal': 1.0, - 'high': 1.5, - 'urgent': 2.0 - }.get(request.real_time_context.urgency, 1.0), - 'capacity_boost': request.real_time_context.current_capacity or {}, - 'performance_boost': request.real_time_context.recent_performance or {} + "urgency_multiplier": {"normal": 1.0, "high": 1.5, "urgent": 2.0}.get(request.real_time_context.urgency, 1.0), + "capacity_boost": request.real_time_context.current_capacity or {}, + "performance_boost": request.real_time_context.recent_performance or {}, } # Step 3: Apply re-ranking model (XGBoost trained on graph + context features) - reranked_candidates = await rerank_with_context( - base_candidates, - context_features - ) + reranked_candidates = await rerank_with_context(base_candidates, context_features) return reranked_candidates ```