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

@ -10,6 +10,7 @@ from pathlib import Path
from typing import Any
from unittest import mock
from docforge.application import CanonicalApplicationService, GenericCanonicalApplier
from docforge.changesets import ChangesetStore
from docforge.errors import DocForgeError
from docforge.project import Project
@ -182,6 +183,91 @@ class DocForgeChangesetTests(unittest.TestCase):
self.assertEqual("delete", delete_store.diff("delete-node")["changes"][0]["operation"])
self.assertEqual(delete_before, self.canonical_digest(delete_root))
def test_hash_bound_application_writes_projection_and_refreshes_derived_state(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = self.copy_fixture(Path(directory))
project = Project.open(root)
store = ChangesetStore(project, "alpha-editor")
hashes = {node.node_id: node.content_hash for node in project.load().nodes}
created = store.create("apply-all")
updated = store.propose_update(
changeset_id="apply-all",
expected_changeset_hash=created["changeset_hash"],
node_id="guide.workflow",
expected_content_hash=hashes["guide.workflow"],
metadata={"summary": "Applied through the canonical application service."},
content="The applied workflow is now canonical.",
relationship_changes=[],
rationale="Exercise a canonical update.",
)
added = store.propose_create(
changeset_id="apply-all",
expected_changeset_hash=updated["changeset_hash"],
node_id="guide.applied",
target_source="docs/content/applied.md",
metadata=self.new_metadata(),
content="This node was created by an approved changeset.",
relationship_changes=[],
rationale="Exercise a canonical creation.",
)
moved = store.propose_move(
changeset_id="apply-all",
expected_changeset_hash=added["changeset_hash"],
node_id="guide.foundation",
expected_content_hash=hashes["guide.foundation"],
target_source="docs/content/foundation-moved.md",
rationale="Exercise a canonical move.",
)
final = store.propose_delete(
changeset_id="apply-all",
expected_changeset_hash=moved["changeset_hash"],
node_id="proof.validation",
expected_content_hash=hashes["proof.validation"],
relationship_changes=[
{
"action": "remove",
"source_id": "proof.validation",
"relation": "proves",
"target_id": "guide.workflow",
}
],
rationale="Exercise a canonical deletion.",
)
service = CanonicalApplicationService(
project,
applier_id="alpha-editor",
applier=GenericCanonicalApplier(project),
)
result = service.apply("apply-all", str(final["changeset_hash"]))
snapshot = project.load()
node_ids = {node.node_id for node in snapshot.nodes}
workflow = next(node for node in snapshot.nodes if node.node_id == "guide.workflow")
self.assertTrue(result["applied"])
self.assertEqual(
[
"docs/content/applied.md",
"docs/content/foundation-moved.md",
"docs/content/foundation.md",
"docs/content/proof.toml",
"docs/content/workflow.md",
],
result["applied_sources"],
)
self.assertEqual({"guide.applied", "guide.foundation", "guide.workflow"}, node_ids)
self.assertEqual(
"Applied through the canonical application service.",
workflow.summary,
)
self.assertFalse((root / "docs/content/foundation.md").exists())
self.assertFalse((root / "docs/content/proof.toml").exists())
self.assertTrue((root / "docs/content/foundation-moved.md").is_file())
self.assertTrue((root / ".docforge/cache/index.sqlite3").is_file())
self.assertTrue((root / ".docforge/rendered/manual.html").is_file())
with self.assertRaisesRegex(DocForgeError, "Canonical project changed"):
service.apply("apply-all", str(final["changeset_hash"]))
def test_optimistic_and_cross_changeset_conflicts_preserve_both_proposals(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = self.copy_fixture(Path(directory))