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

Add gated changeset application and graph controls

This commit is contained in:
Andraxion 2026-07-25 16:00:19 -04:00
parent 3c15e26283
commit 78335c8973
20 changed files with 1813 additions and 453 deletions

View file

@ -6,10 +6,10 @@ import fcntl
import json
import os
import tempfile
from collections.abc import Generator
from collections.abc import Callable, Generator, Mapping
from contextlib import contextmanager
from pathlib import Path
from typing import Any
from typing import Any, cast
from .changeset_contract import (
document_hash,
@ -265,6 +265,68 @@ class ChangesetStore:
)
return projected, document_hash(document)
def apply(
self,
*,
changeset_id: str,
expected_changeset_hash: str,
applier_id: str,
application: Callable[
[ProjectSnapshot, ProjectSnapshot, tuple[Mapping[str, object], ...]],
dict[str, object],
],
) -> dict[str, object]:
"""Apply one exact validated proposal through a project-owned canonical serializer."""
validate_id(changeset_id, "changeset_id")
validate_hash(expected_changeset_hash, "expected_changeset_hash")
if self.writer is None or self.writer.writer_id != applier_id:
raise DocForgeError(
"canonical_application_disabled",
"Canonical applier identity is not configured for this store",
)
with self._lock():
snapshot, document, nodes, edges = self._validate_locked(changeset_id)
actual_hash = document_hash(document)
if actual_hash != expected_changeset_hash:
raise DocForgeError(
"changeset_conflict",
"Changeset changed after the caller approved it",
changeset_id=changeset_id,
expected=expected_changeset_hash,
actual=actual_hash,
)
if document["creator"] != applier_id:
raise DocForgeError(
"changeset_owner_conflict",
"Canonical applier does not own this changeset",
changeset_id=changeset_id,
owner=document["creator"],
applier=applier_id,
)
projected = ProjectSnapshot(
descriptor=snapshot.descriptor,
nodes=tuple(sorted(nodes.values(), key=lambda node: node.node_id)),
edges=tuple(Edge(*edge) for edge in sorted(edges)),
source_hash=snapshot.source_hash,
revision=snapshot.revision,
)
payload = application(
snapshot,
projected,
tuple(cast(Mapping[str, object], item) for item in document["operations"]),
)
current = self.project.load()
return self._result(
current,
document,
valid=True,
applied=True,
applied_from_revision=snapshot.revision,
applied_from_source_hash=snapshot.source_hash,
**payload,
)
def _append(
self,
changeset_id: str,