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

Add versioned effective policy

This commit is contained in:
Andraxion 2026-07-29 06:26:40 -04:00
parent a9a75c5c27
commit 34cd5f74c1
11 changed files with 708 additions and 70 deletions

View file

@ -18,6 +18,7 @@ from .errors import DocForgeError
from .index import ProjectIndex
from .models import IncrementalStateProject, ProjectService, RuntimeValidatedProject
from .pagination import canonical_hash, decode_cursor, page_limit, page_receipt
from .policy import CapabilityMode, capability_mode, compose_effective_policy
from .project import Project, project_root_fingerprint
from .rendering import RenderService
from .telemetry import request, stage
@ -129,50 +130,87 @@ class DocForgeService:
binding_metadata: Mapping[str, object] | None = None,
no_ast: bool = False,
diagnostics: bool = False,
capability_mode_name: str | None = None,
) -> None:
self.project = project
self.index = ProjectIndex(self.project, allow_logic=not no_ast)
self.changesets = ChangesetStore(self.project, proposal_writer)
default_mode: CapabilityMode = (
"application" if canonical_applier is not None else "proposal"
)
selected_mode = capability_mode(capability_mode_name, default=default_mode)
application_enabled = (
canonical_applier_id is not None
and canonical_applier is not None
and selected_mode in {"application", "operator"}
)
self.policy = compose_effective_policy(
selected_mode=selected_mode,
capability_source=("factory_default" if capability_mode_name is None else "explicit"),
no_ast=no_ast,
diagnostics=diagnostics,
render_configured=project.descriptor.render is not None,
application_enabled=application_enabled,
)
self.index = ProjectIndex(self.project, allow_logic=not self.policy.no_ast)
self.changesets = ChangesetStore(
self.project,
proposal_writer if selected_mode != "read" else None,
)
self.rendering = RenderService(self.project, self.changesets)
self.application = CanonicalApplicationService(
self.project,
applier_id=canonical_applier_id,
applier=canonical_applier,
applier_id=canonical_applier_id if application_enabled else None,
applier=canonical_applier if application_enabled else None,
index=self.index,
)
self.visualization = ViewerManagerClient(self.index)
self.context_provider = context_provider
self.binding_metadata = dict(binding_metadata or {})
self.no_ast = no_ast
self.no_ast = self.policy.no_ast
self.diagnostics = diagnostics
self.tool_surface = tool_surface or (
*ALL_TOOLS,
*(APPLICATION_TOOLS if self.application.enabled else ()),
default_surface = (
READ_TOOLS
if selected_mode == "read"
else (
*ALL_TOOLS,
*(
APPLICATION_TOOLS
if self.application.enabled and selected_mode in {"application", "operator"}
else ()
),
)
)
self.tool_surface = tool_surface or default_surface
def adapter_policy(self) -> dict[str, object]:
"""Return the immutable adapter-evolution policy for this MCP binding."""
if not self.no_ast:
return {
"mode": "standard",
"ast_analysis": "allowed",
"logic_projection": "allowed",
"incremental_extraction": "allowed",
"adapter_rewrite": "not_requested",
}
return self.policy.adapter_policy()
def capabilities(self) -> dict[str, object]:
"""Return the registered surfaces separately from startup-bound authority."""
proposal_access = self.changesets.access()
application_access = self.application.access()
return {
"mode": "preserve-no-ast",
"ast_analysis": "forbidden",
"logic_projection": "forbidden",
"incremental_extraction": "allowed",
"adapter_rewrite": "forbidden",
"blocked_tools": ["docforge_get_logic"],
"instruction": (
"Preserve the existing adapter extraction strategy. Do not add Python AST, "
"Tree-sitter, compiler-AST, or function-Logic extraction. Non-AST incremental "
"fingerprinting and caching remain allowed."
),
"schema_version": 1,
"mode": self.policy.capability_mode,
"registered_tools": list(self.tool_surface),
"read": {
"enabled": True,
"tools": [tool for tool in READ_TOOLS if tool in self.tool_surface],
},
"proposal": {
"surface_enabled": any(tool in self.tool_surface for tool in PROPOSAL_TOOLS),
"mutation_access": proposal_access,
},
"application": {
"surface_enabled": any(tool in self.tool_surface for tool in APPLICATION_TOOLS),
"mutation_access": application_access,
},
"operator": {
"enabled": self.policy.capability_mode == "operator",
"tools": [],
},
}
def invoke(
@ -507,15 +545,15 @@ class DocForgeService:
def bootstrap(self) -> dict[str, object]:
def operation() -> dict[str, object]:
synchronized = self.index.synchronize()
snapshot = self.project.load()
root = snapshot.descriptor.root
descriptor = self.project.descriptor
root = descriptor.root
binding = {
"project_root": str(root),
"descriptor_path": str(snapshot.descriptor.descriptor_path),
"adapter": snapshot.descriptor.adapter,
"cache_root": str(snapshot.descriptor.cache_root),
"index_path": str(snapshot.descriptor.index_path),
"changeset_root": str(snapshot.descriptor.changeset_root),
"descriptor_path": str(descriptor.descriptor_path),
"adapter": descriptor.adapter,
"cache_root": str(descriptor.cache_root),
"index_path": str(descriptor.index_path),
"changeset_root": str(descriptor.changeset_root),
**self.binding_metadata,
"adapter_policy": self.adapter_policy(),
}
@ -523,11 +561,19 @@ class DocForgeService:
"docforge_get_context or targeted read tools",
"make and verify one coherent implementation slice",
"docforge_sync",
"docforge_register_changes",
"docforge_get_changeset_diff",
"docforge_apply_changeset",
"docforge_bootstrap",
]
proposal_access = self.changesets.access()
application_access = self.application.access()
if proposal_access["enabled"] and "docforge_register_changes" in self.tool_surface:
recommended_workflow.extend(
(
"docforge_register_changes",
"docforge_get_changeset_diff",
)
)
if application_access["enabled"] and "docforge_apply_changeset" in self.tool_surface:
recommended_workflow.append("docforge_apply_changeset")
recommended_workflow.append("docforge_bootstrap")
if self.no_ast:
recommended_workflow.insert(
1,
@ -536,19 +582,55 @@ class DocForgeService:
"compiler-AST, or function-Logic extraction"
),
)
if descriptor.profiles:
recommended_first_operation: dict[str, object] = {
"tool": "docforge_get_context",
"arguments": {"profile": descriptor.profiles[0].profile_id},
"reason": "Begin with one configured bounded context profile.",
}
else:
recommended_first_operation = {
"tool": "docforge_project_info",
"arguments": dict[str, object](),
"reason": "Confirm the fixed binding before targeted retrieval.",
}
capabilities = self.capabilities()
effective_policy = self.policy.as_dict()
session_contract: dict[str, object] = {
"schema_version": 1,
"binding": binding,
"generation": {
"revision": synchronized["revision"],
"source_hash": synchronized["source_hash"],
"freshness": "current",
},
"effective_policy": effective_policy,
"capabilities": capabilities,
"render_policies": {
"manual": effective_policy["manual_render"],
"graph": effective_policy["graph_render"],
"live_viewer": effective_policy["live_viewer"],
},
"recommended_first_operation": recommended_first_operation,
"recommended_workflow": recommended_workflow,
"prohibitions": effective_policy["prohibitions"],
}
return {
"status": "ok",
"project_id": snapshot.descriptor.project_id,
"project_id": descriptor.project_id,
"project_root_fingerprint": project_root_fingerprint(root),
"title": snapshot.descriptor.title,
"adapter": snapshot.descriptor.adapter,
"revision": snapshot.revision,
"source_hash": snapshot.source_hash,
"title": descriptor.title,
"adapter": descriptor.adapter,
"revision": synchronized["revision"],
"source_hash": synchronized["source_hash"],
"binding": binding,
"canonical_paths": [str(path) for path in snapshot.descriptor.content_roots],
"canonical_paths": [str(path) for path in descriptor.content_roots],
"adapter_policy": self.adapter_policy(),
"proposal_access": self.changesets.access(),
"canonical_application_access": self.application.access(),
"effective_policy": effective_policy,
"capabilities": capabilities,
"session_contract": session_contract,
"proposal_access": proposal_access,
"canonical_application_access": application_access,
"synchronization": synchronized["synchronization"],
"recommended_workflow": recommended_workflow,
}
@ -606,6 +688,8 @@ class DocForgeService:
"Canonical project files own facts; DocForge results are derived."
),
"adapter_policy": self.adapter_policy(),
"effective_policy": self.policy.as_dict(),
"capabilities": self.capabilities(),
"canonical_paths": [
*(relative(path) for path in snapshot.descriptor.content_roots),
*(relative(path) for path in snapshot.descriptor.authority_files),
@ -638,14 +722,20 @@ class DocForgeService:
],
"allowed_tools": list(self.tool_surface),
"excluded_operations": list(
EXCLUDED_OPERATIONS
+ (
("canonical_writes", "canonical_changeset_application")
if not self.application.enabled
else ()
dict.fromkeys(
EXCLUDED_OPERATIONS
+ (
("canonical_writes", "canonical_changeset_application")
if not self.application.enabled
else ()
)
+ (
READ_ONLY_EXCLUDED_OPERATIONS
if self.policy.capability_mode == "read"
else ()
)
+ tuple(self.policy.prohibitions)
)
+ (READ_ONLY_EXCLUDED_OPERATIONS if self.tool_surface == READ_TOOLS else ())
+ (("adapter_ast_upgrade", "function_logic_extraction") if self.no_ast else ())
),
"proposal_access": self.changesets.access(),
"canonical_application_access": self.application.access(),
@ -907,19 +997,20 @@ class DocForgeService:
def _create_bound_server(service: DocForgeService, *, read_only: bool) -> FastMCP:
capability = (
"Read validated documentation for exactly one configured project."
if read_only
else (
"Read validated documentation and write isolated proposal changesets and previews for "
"exactly one configured project"
+ (
", with hash-bound canonical application enabled."
if service.application.enabled
else "."
)
)
)
capability = {
"read": "Read validated documentation for exactly one configured project.",
"proposal": (
"Read validated documentation and use startup-gated isolated proposal changesets and "
"previews for exactly one configured project."
),
"application": (
"Read validated documentation, use startup-gated isolated proposals, and apply one "
"exact validated changeset hash for exactly one configured project."
),
"operator": (
"Operate the fixed validated documentation binding for exactly one configured project."
),
}[service.policy.capability_mode]
server = FastMCP(
"DocForge",
instructions=(
@ -1492,6 +1583,7 @@ def create_server(
canonical_applier_id: str | None = None,
no_ast: bool = False,
diagnostics: bool = False,
capability_mode: str | None = None,
) -> FastMCP:
project = Project.open(project_root)
return create_project_server(
@ -1507,6 +1599,7 @@ def create_server(
},
no_ast=no_ast,
diagnostics=diagnostics,
capability_mode=capability_mode,
)
@ -1520,6 +1613,7 @@ def create_project_server(
binding_metadata: Mapping[str, object] | None = None,
no_ast: bool = False,
diagnostics: bool = False,
capability_mode: str | None = None,
) -> FastMCP:
"""Create the full fixed MCP surface for one explicitly configured project service."""
@ -1532,8 +1626,12 @@ def create_project_server(
binding_metadata=binding_metadata,
no_ast=no_ast,
diagnostics=diagnostics,
capability_mode_name=capability_mode,
)
return _create_bound_server(
service,
read_only=service.policy.capability_mode == "read",
)
return _create_bound_server(service, read_only=False)
def create_read_only_server(
@ -1543,6 +1641,7 @@ def create_read_only_server(
binding_metadata: Mapping[str, object] | None = None,
no_ast: bool = False,
diagnostics: bool = False,
capability_mode: str | None = None,
) -> FastMCP:
"""Create an adapter-capable MCP server exposing only the fixed read tool surface."""
@ -1553,7 +1652,14 @@ def create_read_only_server(
binding_metadata=binding_metadata,
no_ast=no_ast,
diagnostics=diagnostics,
capability_mode_name="read" if capability_mode is None else capability_mode,
)
if service.policy.capability_mode != "read":
raise DocForgeError(
"invalid_capability_mode",
"Read-only server factory accepts only read capability mode",
capability_mode=service.policy.capability_mode,
)
return _create_bound_server(service, read_only=True)
@ -1575,6 +1681,11 @@ def main() -> None:
action="store_true",
help="Attach bounded request-local stage timings and counters",
)
parser.add_argument(
"--capability-mode",
choices=("read", "proposal", "application", "operator"),
help="Expose the versioned project-bound capability surface",
)
arguments = parser.parse_args()
create_server(
arguments.project_root,
@ -1582,6 +1693,7 @@ def main() -> None:
canonical_applier_id=arguments.canonical_applier,
no_ast=arguments.no_ast,
diagnostics=arguments.diagnostics,
capability_mode=arguments.capability_mode,
).run(transport="stdio")

166
src/docforge/policy.py Normal file
View file

@ -0,0 +1,166 @@
"""Versioned immutable policy composition for one project-bound server."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Literal
from .errors import DocForgeError
CapabilityMode = Literal["read", "proposal", "application", "operator"]
CAPABILITY_MODES: tuple[CapabilityMode, ...] = (
"read",
"proposal",
"application",
"operator",
)
POLICY_PRECEDENCE = (
"core_safety",
"explicit_binding",
"no_ast_shorthand",
"resource_availability",
)
def capability_mode(value: str | None, *, default: CapabilityMode) -> CapabilityMode:
"""Validate one additive capability-mode selection."""
selected = default if value is None else value
if selected not in CAPABILITY_MODES:
raise DocForgeError(
"invalid_capability_mode",
"Capability mode is unsupported",
capability_mode=selected,
allowed=list(CAPABILITY_MODES),
)
return selected # type: ignore[return-value]
@dataclass(frozen=True)
class EffectivePolicyV1:
"""One fully composed process policy shared by every public projection."""
capability_mode: CapabilityMode
capability_source: Literal["factory_default", "explicit"]
adapter_evolution: Literal["allowed", "preserve"]
ast_analysis: Literal["allowed", "forbidden"]
logic_indexing: Literal["full", "off"]
synchronization: Literal["automatic"]
integrity: Literal["validated"]
manual_render: Literal["auto", "explicit", "disabled"]
graph_render: Literal["disabled"]
live_viewer: Literal["on-demand"]
profiling: Literal["enabled", "disabled"]
blocked_tools: tuple[str, ...]
prohibitions: tuple[str, ...]
@property
def no_ast(self) -> bool:
return self.ast_analysis == "forbidden"
def as_dict(self) -> dict[str, object]:
return {
"schema_version": 1,
"capability_mode": self.capability_mode,
"capability_source": self.capability_source,
"adapter_evolution": self.adapter_evolution,
"ast_analysis": self.ast_analysis,
"logic_indexing": self.logic_indexing,
"synchronization": self.synchronization,
"integrity": self.integrity,
"manual_render": self.manual_render,
"graph_render": self.graph_render,
"live_viewer": self.live_viewer,
"profiling": self.profiling,
"blocked_tools": list(self.blocked_tools),
"prohibitions": list(self.prohibitions),
"precedence": list(POLICY_PRECEDENCE),
}
def adapter_policy(self) -> dict[str, object]:
"""Preserve the exact legacy adapter-policy projection."""
if not self.no_ast:
return {
"mode": "standard",
"ast_analysis": "allowed",
"logic_projection": "allowed",
"incremental_extraction": "allowed",
"adapter_rewrite": "not_requested",
}
return {
"mode": "preserve-no-ast",
"ast_analysis": "forbidden",
"logic_projection": "forbidden",
"incremental_extraction": "allowed",
"adapter_rewrite": "forbidden",
"blocked_tools": ["docforge_get_logic"],
"instruction": (
"Preserve the existing adapter extraction strategy. Do not add Python AST, "
"Tree-sitter, compiler-AST, or function-Logic extraction. Non-AST incremental "
"fingerprinting and caching remain allowed."
),
}
def compose_effective_policy(
*,
selected_mode: CapabilityMode,
capability_source: Literal["factory_default", "explicit"],
no_ast: bool,
diagnostics: bool,
render_configured: bool,
application_enabled: bool,
) -> EffectivePolicyV1:
"""Compose fixed defaults with restrictive compatibility shorthands."""
if selected_mode == "application" and not application_enabled:
raise DocForgeError(
"capability_unavailable",
"Application capability requires a startup-bound canonical applier",
capability_mode=selected_mode,
required="canonical_applier",
)
prohibitions = [
"arbitrary_file_access",
"arbitrary_renderer_execution",
"shell_execution",
"git_mutation",
"deployment",
"publication",
"project_switching",
]
blocked_tools: tuple[str, ...] = ()
if no_ast:
prohibitions.extend(
(
"adapter_ast_upgrade",
"tree_sitter_upgrade",
"compiler_ast_upgrade",
"function_logic_extraction",
)
)
blocked_tools = ("docforge_get_logic",)
manual_render: Literal["auto", "explicit", "disabled"]
if not render_configured:
manual_render = "disabled"
elif application_enabled and selected_mode in {"application", "operator"}:
manual_render = "auto"
else:
manual_render = "explicit"
return EffectivePolicyV1(
capability_mode=selected_mode,
capability_source=capability_source,
adapter_evolution="preserve" if no_ast else "allowed",
ast_analysis="forbidden" if no_ast else "allowed",
logic_indexing="off" if no_ast else "full",
synchronization="automatic",
integrity="validated",
manual_render=manual_render,
graph_render="disabled",
live_viewer="on-demand",
profiling="enabled" if diagnostics else "disabled",
blocked_tools=blocked_tools,
prohibitions=tuple(prohibitions),
)