166 lines
5.6 KiB
Python
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),
|
|
)
|