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

Add explicit cross-identity proposal acceptance

This commit is contained in:
Andraxion 2026-08-01 02:53:15 -04:00
parent 377cca0531
commit 7b21541ab3
10 changed files with 247 additions and 8 deletions

View file

@ -17,6 +17,7 @@ from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from mcp.shared.memory import create_connected_server_and_client_session
from docforge.application import GenericCanonicalApplier
from docforge.changesets import ChangesetStore
from docforge.errors import DocForgeError
from docforge.index import ProjectIndex
@ -29,6 +30,7 @@ from docforge.mcp_server import (
SERVER_VERSION,
DocForgeService,
_create_bound_server,
create_project_server,
create_server,
)
from docforge.project import Project, project_root_fingerprint
@ -1446,6 +1448,83 @@ class DocForgeMcpTests(unittest.IsolatedAsyncioTestCase):
self.assertEqual("Applied through the gated MCP tool.", workflow.summary)
self.assertTrue((root / ".docforge/rendered/manual.html").is_file())
async def test_project_server_accepts_an_explicit_contributor_without_granting_apply(
self,
) -> None:
with tempfile.TemporaryDirectory() as directory:
root = self.copy_fixture("alpha", Path(directory))
descriptor = root / ".docforge/project.toml"
descriptor.write_text(
descriptor.read_text(encoding="utf-8")
+ """
[[changesets.writers]]
id = "contributor"
families = ["guide"]
operations = ["update"]
""",
encoding="utf-8",
)
project = Project.open(root)
ProjectIndex(project).build()
async with create_connected_server_and_client_session(
create_server(
root,
"contributor",
capability_mode="proposal",
),
raise_exceptions=True,
) as contributor:
contributor_tools = tuple(
tool.name for tool in (await contributor.list_tools()).tools
)
registered = await contributor.call_tool(
"docforge_register_changes",
{
"changeset_id": "accepted-contribution",
"operations": [
{
"operation": "update",
"node_id": "guide.workflow",
"metadata": {"summary": "Accepted through a separate applier."},
"rationale": "Prove explicit contributor acceptance over MCP.",
}
],
},
)
async with create_connected_server_and_client_session(
create_project_server(
project,
proposal_writer="alpha-editor",
canonical_applier_id="alpha-editor",
canonical_applier=GenericCanonicalApplier(project),
accepted_proposal_writers=("contributor",),
capability_mode="application",
),
raise_exceptions=True,
) as developer:
contract = await developer.call_tool("docforge_get_contract", {})
applied = await developer.call_tool(
"docforge_apply_changeset",
{
"changeset_id": "accepted-contribution",
"expected_changeset_hash": registered.structuredContent["changeset_hash"],
},
)
self.assertEqual(ALL_TOOLS, contributor_tools)
self.assertNotIn("docforge_apply_changeset", contributor_tools)
self.assertEqual(
["contributor"],
contract.structuredContent["canonical_application_access"][
"accepted_proposal_writers"
],
)
self.assertTrue(applied.structuredContent["applied"])
self.assertEqual("contributor", applied.structuredContent["proposal_creator"])
self.assertEqual("alpha-editor", applied.structuredContent["applied_by"])
async def test_stdio_transport_serves_the_same_project_bound_contract(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = self.copy_fixture("beta", Path(directory))