1
0
Fork 0
Code Issues Pull requests Projects Releases 2 Packages Wiki Activity Actions Pages

Bound adapter assemblies and extraction caches

This commit is contained in:
Andraxion 2026-07-29 14:52:38 -04:00
parent 9cd7c4e424
commit f00aa65a73
4 changed files with 232 additions and 8 deletions

View file

@ -21,6 +21,8 @@ from .adapter_validation import (
from .config_validation import ID_PATTERN
from .errors import DocForgeError
from .incremental import (
MAX_EXTRACTION_CACHE_BYTES,
MAX_EXTRACTION_CACHE_SOURCES,
CachedSource,
ExtractionCache,
affected_sources,
@ -40,6 +42,10 @@ from .models import (
)
from .telemetry import increment, stage
MAX_ADAPTER_EDGE_MULTIPLIER = 32
MAX_ADAPTER_LOGIC_NODE_MULTIPLIER = 32
MAX_ADAPTER_LOGIC_EDGE_MULTIPLIER = 64
@dataclass(frozen=True)
class AdapterNode:
@ -464,6 +470,9 @@ class AdapterProject:
canonical_sources,
captured,
)
self._enforce_assembly_limits(
AdapterAssembly(projection=projection, logic=self._last_logic)
)
return ProjectSnapshot(
descriptor=self.descriptor,
nodes=projection.core_nodes(),
@ -628,11 +637,14 @@ class AdapterProject:
validate_manifest(manifest)
if manifest.identity() != self._identity:
raise DocForgeError("adapter_changed", "Adapter identity changed during the operation")
self._enforce_manifest_limits(manifest)
cache = load_extraction_cache(
self._cache_path,
project_id=manifest.project_id,
adapter_id=manifest.adapter_id,
adapter_version=manifest.adapter_version,
max_bytes=MAX_EXTRACTION_CACHE_BYTES,
max_sources=MAX_EXTRACTION_CACHE_SOURCES,
)
cached = {source.source_id: source for source in cache.sources} if cache else {}
current = {source.source_id: source for source in manifest.sources}
@ -737,6 +749,7 @@ class AdapterProject:
"Incremental assembly changed the manifest-bound project identity",
)
_validate_adapter_assembly(assembly)
self._enforce_assembly_limits(assembly)
stable = loader.load_manifest()
validate_manifest(stable)
if stable != manifest:
@ -752,6 +765,8 @@ class AdapterProject:
adapter_version=manifest.adapter_version,
sources=tuple(cache_records),
),
max_bytes=MAX_EXTRACTION_CACHE_BYTES,
max_sources=MAX_EXTRACTION_CACHE_SOURCES,
)
self._last_logic = logic_projections
self._last_build_report = {
@ -766,6 +781,40 @@ class AdapterProject:
}
return assembly
def _enforce_manifest_limits(self, manifest: AdapterManifest) -> None:
if len(manifest.sources) > MAX_EXTRACTION_CACHE_SOURCES:
raise DocForgeError(
"adapter_limit",
"Adapter manifest exceeds the bounded source limit",
maximum=MAX_EXTRACTION_CACHE_SOURCES,
actual=len(manifest.sources),
)
def _enforce_assembly_limits(self, assembly: AdapterAssembly) -> None:
maximum_nodes = self.descriptor.limits.max_nodes
maximum_edges = maximum_nodes * MAX_ADAPTER_EDGE_MULTIPLIER
maximum_logic_nodes = maximum_nodes * MAX_ADAPTER_LOGIC_NODE_MULTIPLIER
maximum_logic_edges = maximum_nodes * MAX_ADAPTER_LOGIC_EDGE_MULTIPLIER
actual_nodes = len(assembly.projection.nodes)
actual_edges = len(assembly.projection.edges)
actual_logic_nodes = sum(len(projection.nodes) for projection in assembly.logic)
actual_logic_edges = sum(len(projection.edges) for projection in assembly.logic)
limits = (
("nodes", actual_nodes, maximum_nodes),
("edges", actual_edges, maximum_edges),
("logic_nodes", actual_logic_nodes, maximum_logic_nodes),
("logic_edges", actual_logic_edges, maximum_logic_edges),
)
for label, actual, maximum in limits:
if actual > maximum:
raise DocForgeError(
"adapter_limit",
f"Adapter assembly exceeds the configured {label.replace('_', ' ')} limit",
kind=label,
maximum=maximum,
actual=actual,
)
def canonical_source_paths(self) -> tuple[Path, ...]:
"""Adapters validate their own source sets before producing a projection."""