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 typing import Protocol, runtime_checkable
from .adapter_validation import (
source_payload,
source_projection,
validate_logic_projection,
validate_manifest,
validate_projection,
validate_source_projection,
@ -136,6 +137,14 @@ class AdapterSourceProjection:
logic: tuple[LogicProjection, ...] = ()
@dataclass(frozen=True)
class AdapterAssembly:
"""One finalized graph and Logic set assembled from cached source contributions."""
projection: AdapterProjection
logic: tuple[LogicProjection, ...] = ()
class AdapterLoader(Protocol):
"""Load one current, deterministic, project-confined adapter projection."""
@ -151,6 +160,17 @@ class IncrementalAdapterLoader(AdapterLoader, Protocol):
def extract_source(self, source: AdapterSource) -> AdapterSourceProjection: ...
@runtime_checkable
class IncrementalAdapterAssembler(Protocol):
"""Optionally normalize overlapping source evidence into one final projection."""
def assemble_projection(
self,
manifest: AdapterManifest,
contributions: tuple[AdapterSourceProjection, ...],
) -> AdapterAssembly: ...
ProposalValidator = Callable[
[
ProjectSnapshot,
@ -503,43 +523,78 @@ class AdapterProject:
validate_source_projection(source, contribution)
contributions.append(contribution)
cache_records.append(cache_record)
projection = 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=tuple(
if isinstance(loader, IncrementalAdapterAssembler):
assembly = loader.assemble_projection(manifest, tuple(contributions))
projection = assembly.projection
logic_projections = tuple(
sorted(
(node for contribution in contributions for node in contribution.nodes),
key=lambda item: item.node.node_id,
assembly.logic,
key=lambda projection: projection.owner_node_id,
)
),
edges=tuple(
sorted(
(edge for contribution in contributions for edge in contribution.edges),
key=lambda item: (
item.edge.source_id,
item.edge.relation,
item.edge.target_id,
),
)
),
)
validate_projection(projection)
logic_projections = tuple(
sorted(
(logic for contribution in contributions for logic in contribution.logic),
key=lambda projection: projection.owner_node_id,
)
else:
projection = 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=tuple(
sorted(
(node for contribution in contributions for node in contribution.nodes),
key=lambda item: item.node.node_id,
)
),
edges=tuple(
sorted(
(edge for contribution in contributions for edge in contribution.edges),
key=lambda item: (
item.edge.source_id,
item.edge.relation,
item.edge.target_id,
),
)
),
)
logic_projections = tuple(
sorted(
(logic for contribution in contributions for logic in contribution.logic),
key=lambda projection: projection.owner_node_id,
)
)
identity = (
projection.project_id,
projection.adapter_id,
projection.adapter_version,
projection.root,
)
if (
identity != manifest.identity()
or projection.title != manifest.title
or projection.revision != manifest.revision
or projection.source_hash != manifest.source_hash
):
raise DocForgeError(
"invalid_adapter",
"Incremental assembly changed the manifest-bound project identity",
)
validate_projection(projection)
owners = [projection.owner_node_id for projection in logic_projections]
if len(owners) != len(set(owners)):
raise DocForgeError(
"invalid_adapter", "A primary graph node may own only one logic projection"
)
node_ids = {item.node.node_id for item in projection.nodes}
for logic_projection in logic_projections:
validate_logic_projection(logic_projection)
if logic_projection.owner_node_id not in node_ids:
raise DocForgeError(
"invalid_adapter",
"A Logic projection owner must exist in the assembled primary graph",
owner_node_id=logic_projection.owner_node_id,
)
stable = loader.load_manifest()
validate_manifest(stable)
if stable != manifest: