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

Add deterministic incremental adapter assembly

This commit is contained in:
Andraxion 2026-07-27 16:01:40 -04:00
parent 09c09300b1
commit cd54cae71d
6 changed files with 224 additions and 38 deletions

View file

@ -12,6 +12,7 @@ from pathlib import Path
from mcp.shared.memory import create_connected_server_and_client_session
from docforge.adapter_contract import (
AdapterAssembly,
AdapterEdge,
AdapterManifest,
AdapterNode,
@ -182,6 +183,56 @@ class IncrementalLoader:
)
class OverlappingIncrementalLoader(IncrementalLoader):
def extract_source(self, source: AdapterSource) -> AdapterSourceProjection:
self.extract_calls.append(source.source_id)
content = self.sources[source.source_id]
shared = Node(
node_id="guide.shared",
title="Shared",
family="guide",
authority="derived",
status="active",
tags=("guide",),
summary=f"Evidence selected from {source.source_id}.",
content=content,
source_path=source.source_path,
source_anchor=None,
content_hash=self._hash(content),
)
return AdapterSourceProjection(
source_id=source.source_id,
fingerprint=source.fingerprint,
nodes=(AdapterNode(shared),),
edges=(),
)
def assemble_projection(
self,
manifest: AdapterManifest,
contributions: tuple[AdapterSourceProjection, ...],
) -> AdapterAssembly:
selected = min(contributions, key=lambda item: item.source_id)
return AdapterAssembly(
AdapterProjection(
project_id=manifest.project_id,
title=manifest.title,
adapter_id=manifest.adapter_id,
adapter_version=manifest.adapter_version,
root=manifest.root,
revision=manifest.revision,
source_hash=manifest.source_hash,
nodes=selected.nodes,
edges=(),
)
)
def load_projection(self) -> AdapterProjection:
manifest = self.load_manifest()
contributions = tuple(self.extract_source(source) for source in manifest.sources)
return self.assemble_projection(manifest, contributions).projection
class AdapterContractTests(unittest.TestCase):
def projection(self, root: Path) -> AdapterProjection:
foundation = Node(
@ -426,6 +477,38 @@ class AdapterContractTests(unittest.TestCase):
self.assertEqual("ok", equivalent["status"])
self.assertEqual(1, equivalent["node_count"])
def test_incremental_adapter_can_assemble_overlapping_source_evidence(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory).resolve()
loader = OverlappingIncrementalLoader(root)
project = AdapterProject(loader, cache_root=root / ".cache" / "overlap")
index = ProjectIndex(project)
first = index.build()
self.assertEqual(2, first["build"]["reparsed_sources"])
self.assertEqual(1, first["node_count"])
self.assertEqual(
"Foundation content.",
index.get_node("guide.shared")["node"]["content"],
)
loader.extract_calls.clear()
warm = index.build()
self.assertEqual(2, warm["build"]["cache_hits"])
self.assertEqual([], loader.extract_calls)
loader.sources["guide.workflow"] = "Changed overlapping evidence."
loader.extract_calls.clear()
changed = index.build()
self.assertEqual(["guide.workflow"], loader.extract_calls)
self.assertEqual(1, changed["build"]["reparsed_sources"])
self.assertEqual(1, changed["node_count"])
self.assertEqual(
"Foundation content.",
index.get_node("guide.shared")["node"]["content"],
)
self.assertEqual("ok", project.verify_incremental_equivalence()["status"])
def test_artifact_comparison_is_complete_and_byte_exact(self) -> None:
reference = (
ShadowArtifact("manual", b"same"),