"""EngineShardAdapter — the engine exposed as a canonical-mode shard (WikiEngineCoreArchitecture §6, E-1/E-5). The engine is *one shard*: the orchestrator consumes it only through this `ShardAdapter`. The adapter is backed by the kernel (T1) + a composed extension set (T2/T3); its §A capability profile is **derived from the active extensions** (T4), so the orchestrator sees an honest, conformance-verifiable profile that reflects exactly what is activated. Read/write run the extensions' transform hooks; everything above this stays in the orchestrator (no union/projection import). """ from __future__ import annotations from collections.abc import Iterable from shard_wiki.adapters import ShardAdapter from shard_wiki.engine.activation import ActivationContext, ActivationProvider, ActivationResolver from shard_wiki.engine.extension import ActiveExtensions, ExtensionRuntime, Hook from shard_wiki.engine.kernel import EngineKernel from shard_wiki.engine.profile import derive_profile from shard_wiki.model import CapabilityProfile, NotSupported, Page, Verb __all__ = ["EngineShardAdapter", "build_engine_shard"] class EngineShardAdapter(ShardAdapter): def __init__( self, kernel: EngineKernel, active: ActiveExtensions, base_profile: CapabilityProfile | None = None, ) -> None: self._kernel = kernel self._active = active self._profile = derive_profile(active, base_profile) # validated (E-5) @property def shard_id(self) -> str: return self._kernel.shard_id def profile(self) -> CapabilityProfile: return self._profile def keys(self) -> Iterable[str]: return self._kernel.keys() def read(self, key: str) -> Page: page = self._kernel.read(key) return self._active.dispatch_transform(Hook.ON_READ, page, {"shard_id": self.shard_id}) def current_rev(self, key: str) -> str | None: return self._kernel.current_rev(key) def write(self, key: str, body: str) -> Page: if not self._profile.supports(Verb.WRITE): raise NotSupported(f"{type(self).__name__} ({self.shard_id}) is read-only") body = self._active.dispatch_transform( Hook.ON_WRITE, body, {"shard_id": self.shard_id, "key": key} ) return self._kernel.write(key, body) def build_engine_shard( shard_id: str, runtime: ExtensionRuntime, *, activate: Iterable[str] | None = None, provider: ActivationProvider | None = None, context: ActivationContext | None = None, base_profile: CapabilityProfile | None = None, ) -> EngineShardAdapter: """Stand up an engine shard: resolve which extensions are active (explicit ``activate`` ids, or via an activation ``provider`` over the runtime's available set), compose them, and wrap a fresh kernel as a `ShardAdapter`. """ if provider is not None: ctx = context or ActivationContext(shard_id) ids = ActivationResolver(provider).active_extensions(runtime.available(), ctx) else: ids = set(activate or ()) active = runtime.activate(ids) return EngineShardAdapter(EngineKernel(shard_id), active, base_profile)