feat(coordination): patch rendering (unified diff) (WP-0008 T3)

render_patch(overlay, base_body) → Patch (pure difflib unified diff, target-
labelled); is_empty when unchanged. 3 tests green, pyflakes clean. (ADR-05)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-15 12:32:47 +02:00
parent d797bc5ee4
commit 715ab1ca00
4 changed files with 71 additions and 1 deletions

View File

@@ -7,6 +7,7 @@ from shard_wiki.coordination.decision_log import (
EventType,
)
from shard_wiki.coordination.overlay import Overlay, OverlayEngine
from shard_wiki.coordination.patch import Patch, render_patch
__all__ = [
"DecisionLog",
@@ -15,4 +16,6 @@ __all__ = [
"CoordinationState",
"Overlay",
"OverlayEngine",
"Patch",
"render_patch",
]

View File

@@ -0,0 +1,38 @@
"""Patch rendering — an overlay as a reviewable diff (FederationRequirements ADR-05).
A pure function over (base body, overlay body): the auditable change proposal that an overlay
becomes before it is applied. Markdown/native text in this slice (lossless); a lossy
native-syntax-with-fidelity-report variant is later (TSD §A.6).
"""
from __future__ import annotations
import difflib
from dataclasses import dataclass
from shard_wiki.coordination.overlay import Overlay
from shard_wiki.model import Identity
__all__ = ["Patch", "render_patch"]
@dataclass(frozen=True, slots=True)
class Patch:
target: Identity
diff: str
@property
def is_empty(self) -> bool:
return self.diff == ""
def render_patch(overlay: Overlay, base_body: str) -> Patch:
"""Render ``base_body`` → ``overlay.body`` as a unified diff against the overlay target."""
label = str(overlay.target)
lines = difflib.unified_diff(
base_body.splitlines(keepends=True),
overlay.body.splitlines(keepends=True),
fromfile=f"a/{label}",
tofile=f"b/{label}",
)
return Patch(target=overlay.target, diff="".join(lines))