feat(space): wire write path into InformationSpace; integration (WP-0008 T6)

edit()/overlay()/apply_overlay() on InformationSpace. edit() unifies the write
path through one principled route — draft overlay then apply: write-through-capable
target fast-forwards (APPLIED), read-only target keeps the draft as local truth
(KEPT_DRAFT), external drift refuses (no clobber). Integration tests cover all
four. 64 tests green, pyflakes clean. Flips WP-0008 done.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-15 13:46:32 +02:00
parent 4be2f190a0
commit 3ea0cc1226
5 changed files with 94 additions and 7 deletions

View File

@@ -65,7 +65,7 @@ class UnionGraph:
def attach(self, adapter: ShardAdapter) -> None:
self._shards.append(adapter)
def _shard(self, shard_id: str) -> ShardAdapter | None:
def shard(self, shard_id: str) -> ShardAdapter | None:
return next((s for s in self._shards if s.shard_id == shard_id), None)
def _read_all(self, key: str) -> list[Page]:
@@ -88,7 +88,7 @@ class UnionGraph:
target = state.resolve_alias(name)
if target is not None and ":" in target:
shard_id, _, key = target.partition(":")
shard = self._shard(shard_id)
shard = self.shard(shard_id)
if shard is not None:
try:
page = self._with_overlay(shard.read(key), overlays)
@@ -127,7 +127,7 @@ class UnionGraph:
have = {p.identity for p in existing}
out: list[Page] = []
for identity, overlay in overlays.items():
if identity.key != name or identity in have or self._shard(identity.shard) is None:
if identity.key != name or identity in have or self.shard(identity.shard) is None:
continue
env = ProvenanceEnvelope(
source_shard=identity.shard,