generated from coulomb/repo-seed
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:
@@ -9,7 +9,13 @@ a network API is a later workplan.
|
||||
from __future__ import annotations
|
||||
|
||||
from shard_wiki.adapters import ShardAdapter, assert_conformant
|
||||
from shard_wiki.coordination import DecisionLog, EventType
|
||||
from shard_wiki.coordination import (
|
||||
ApplyResult,
|
||||
DecisionLog,
|
||||
EventType,
|
||||
Overlay,
|
||||
OverlayEngine,
|
||||
)
|
||||
from shard_wiki.model import Page
|
||||
from shard_wiki.policy import DEFAULT_POLICY, Policy
|
||||
from shard_wiki.union import Resolution, UnionGraph
|
||||
@@ -22,6 +28,7 @@ class InformationSpace:
|
||||
self.space_id = space_id
|
||||
self.log = DecisionLog()
|
||||
self.union = UnionGraph(space_id, log=self.log, policy=policy)
|
||||
self.overlays = OverlayEngine(space_id, self.log)
|
||||
|
||||
def attach(self, adapter: ShardAdapter) -> None:
|
||||
"""Attach a shard — only if it passes conformance (verified profile, I-3/§6.6)."""
|
||||
@@ -40,3 +47,25 @@ class InformationSpace:
|
||||
def read(self, name: str) -> Page:
|
||||
"""Resolve and return the page (or the canonical pick of a chorus). KeyError if red-link."""
|
||||
return self.union.resolve(name).single()
|
||||
|
||||
def overlay(self, name: str, body: str, actor: str | None = None) -> Overlay:
|
||||
"""Draft a non-destructive overlay against the resolved page (overlay-before-mutation)."""
|
||||
page = self.read(name)
|
||||
return self.overlays.draft(page.identity, body, page.envelope.source_rev, actor=actor)
|
||||
|
||||
def apply_overlay(self, overlay_id: str) -> ApplyResult:
|
||||
"""Apply a draft overlay to its target shard (apply-under-drift, §8.6)."""
|
||||
overlay = self.overlays.get(overlay_id)
|
||||
if overlay is None:
|
||||
raise KeyError(overlay_id)
|
||||
adapter = self.union.shard(overlay.target.shard)
|
||||
if adapter is None:
|
||||
raise KeyError(overlay.target.shard)
|
||||
return self.overlays.apply(overlay_id, adapter)
|
||||
|
||||
def edit(self, name: str, body: str, actor: str | None = None) -> ApplyResult:
|
||||
"""Edit a page through the one principled path: draft an overlay, then apply it. A
|
||||
write-through-capable target fast-forwards (write-through); a read-only target keeps the
|
||||
draft as local truth (I-5: overlay before mutation, always)."""
|
||||
overlay = self.overlay(name, body, actor=actor)
|
||||
return self.apply_overlay(overlay.overlay_id)
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user