|
| 1 | +# Governance Integration Point |
| 2 | + |
| 3 | +`uipath-runtime` wraps runtimes with governance via a single direct |
| 4 | +function, `apply_governance_wrapper`, gated by the |
| 5 | +`EnablePythonGovernanceChecker` feature flag. |
| 6 | + |
| 7 | +Governance contracts (feature-flag, exceptions, models) live in |
| 8 | +`uipath.core.governance` (in `uipath-core`); the runtime-side wrapper |
| 9 | +lives here in `uipath.runtime.governance`. Runtime has **no separate |
| 10 | +`uipath-governance` dependency** — the contracts namespace is always |
| 11 | +available because `uipath-core` is already a hard dep. When the flag |
| 12 | +is off, `uipath.runtime.governance.wrapper` is **not imported** — its |
| 13 | +transitive cost stays off the startup path. |
| 14 | + |
| 15 | +## How it works |
| 16 | + |
| 17 | +``` |
| 18 | +UiPathRuntimeFactoryRegistry.get(...) |
| 19 | + ↓ returns |
| 20 | +UiPathWrappedRuntimeFactory.new_runtime(...) |
| 21 | + ↓ calls |
| 22 | +apply_governance_wrapper(runtime, context, runtime_id) |
| 23 | + ↓ |
| 24 | + if _is_governance_enabled(): |
| 25 | + from uipath.runtime.governance.wrapper import governance_wrapper # lazy |
| 26 | + return governance_wrapper(runtime, context, runtime_id) |
| 27 | + else: |
| 28 | + return runtime # unwrapped, no governance import |
| 29 | +``` |
| 30 | + |
| 31 | +## Feature flag |
| 32 | + |
| 33 | +| Setting | Effect | |
| 34 | +|---|---| |
| 35 | +| `FeatureFlags.configure_flags({"EnablePythonGovernanceChecker": True})` (typically via gitops) | Governance is applied | |
| 36 | +| `UIPATH_FEATURE_EnablePythonGovernanceChecker=true` env var | Governance is applied (fallback when no programmatic config) | |
| 37 | +| Neither set | Governance **not** applied; `uipath.runtime.governance.wrapper` is **not imported** | |
| 38 | + |
| 39 | +Resolution and fallback semantics come from `uipath-core`'s |
| 40 | +`FeatureFlags.is_flag_enabled(..., default=False)`. Programmatic |
| 41 | +configuration beats env var. |
| 42 | + |
| 43 | +## API |
| 44 | + |
| 45 | +```python |
| 46 | +from uipath.runtime import ( |
| 47 | + GOVERNANCE_FEATURE_FLAG, # "EnablePythonGovernanceChecker" |
| 48 | + apply_governance_wrapper, # the call-site |
| 49 | +) |
| 50 | +``` |
| 51 | + |
| 52 | +`apply_governance_wrapper(runtime, context, runtime_id)` is an |
| 53 | +`async` function. It returns the original runtime untouched when the |
| 54 | +flag is off or when the wrapper itself raises — governance failures |
| 55 | +must never break agent execution. |
| 56 | + |
| 57 | +## Why deferred-import matters |
| 58 | + |
| 59 | +When the flag is off, `apply_governance_wrapper` returns before the |
| 60 | +`from uipath.runtime.governance.wrapper import governance_wrapper` line |
| 61 | +ever runs. That keeps governance's transitive imports — audit, |
| 62 | +evaluator, OpenTelemetry, the policy index — entirely off the startup |
| 63 | +hot path. |
| 64 | + |
| 65 | +## Testing |
| 66 | + |
| 67 | +Force the flag on/off per test via `FeatureFlags`: |
| 68 | + |
| 69 | +```python |
| 70 | +from uipath.core.feature_flags import FeatureFlags |
| 71 | +from uipath.runtime.wrapper import GOVERNANCE_FEATURE_FLAG |
| 72 | + |
| 73 | +# Force enable |
| 74 | +FeatureFlags.configure_flags({GOVERNANCE_FEATURE_FLAG: True}) |
| 75 | + |
| 76 | +# Force disable |
| 77 | +FeatureFlags.configure_flags({GOVERNANCE_FEATURE_FLAG: False}) |
| 78 | + |
| 79 | +# Reset (typically in a teardown fixture) |
| 80 | +FeatureFlags.reset_flags() |
| 81 | +``` |
| 82 | + |
| 83 | +Use `sys.modules` patching to stub `uipath.runtime.governance.wrapper` |
| 84 | +when you need to assert against the wrapper invocation without |
| 85 | +actually importing it — see `tests/test_wrapper.py` for the fixture. |
0 commit comments