1
0
Fork 0
Code Issues Pull requests Projects Releases 2 Packages Wiki Activity Actions Pages
DocForge2/src/docforge/policy.py

166 lines
5.6 KiB
Python

"""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),
)