symfonic.platform.extension_policy_stage¶
extension_policy_stage ¶
The rung a contributed extension policy is actually asked on (#147).
ComposedExtensions.decide_detailed has walked contributed policies for a
long time -- honouring a deny, recording an abstention, allowing when nobody
objects. What was missing was a caller on a kernel turn.
:meth:~symfonic.platform.extensions.ExtensionCapability.contribute returned
tools only, while its own docstring noted that CapabilityContribution
carries "tools, stages, handlers and grants". So a deployment could contribute
a policy that composed cleanly, reported success, and was consulted before
nothing.
Why pre-tool, for governance.effect's reason rather than by
analogy: it sits after the kernel mints the call ids and before the calls
execute, so a refusal there prevents the call instead of describing one that
already ran.
Why a refusal raises. It is the mechanism the sibling enforcement point
already uses -- governance_verdict raises GovernanceRefused from the
same rung. Returning an amended batch minus the denied call was the
alternative and is worse: the model asked for a tool and would get silence,
with nothing in the transcript saying it was refused or by whom.
Why an abstention denies, which is the one real choice here.
ComposedExtensions deliberately turns a policy that raises into an
abstention so a single broken guard is not a total outage, and harvest.py
records that what an abstention costs belongs to the enforcement point
rather than to the composition. The legacy enforcement point
(cutover/guardrails.py) denies on one. This one denies too -- not because
denying is safer in the abstract, but because two routes disagreeing over
whether a guard being down means "allow" would be a worse defect than either
answer alone.
ExtensionPolicyRefused ¶
Bases: ExtensionError
A contributed policy denied an action, and the action did not happen.
policy_handler ¶
Build the handler that asks composed's policies about each call.
Source code in src/symfonic/platform/extension_policy_stage.py
policy_stage ¶
The pre-tool stage a contributed policy is asked in.
Declares no effect family: asking a policy performs none of the kernel's effects, and declaring one it was not granted would be refused at fold.