Skip to content

feat(governance): delegation guard#125

Closed
aditik0303 wants to merge 3 commits into
feat/governance-evaluatorfrom
feat/governance-delegation-guard
Closed

feat(governance): delegation guard#125
aditik0303 wants to merge 3 commits into
feat/governance-evaluatorfrom
feat/governance-delegation-guard

Conversation

@aditik0303

Copy link
Copy Markdown

Stacked PR 6/7 — part of splitting feat/governance-core into reviewable slices. Base: feat/governance-evaluator. One logical slice (branch is cumulative so CI is green). Merge in order #1#7 and delete each branch on merge so the next PR auto-retargets onto feat/agentic-governance. feat/governance-core kept untouched as backup.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds an async-aware delegation depth guard for agents, ensuring delegation depth is enforced consistently across both sync (invoke) and async (ainvoke) call paths while avoiding per-agent ContextVar leaks.

Changes:

  • Implement delegation guard that wraps both invoke and ainvoke, preserving coroutine-ness for async methods.
  • Track delegation depth via a single module-level ContextVar keyed by id(agent) and clean up entries on uninstall.
  • Add comprehensive tests covering sync/async behavior, lifecycle (install/uninstall), env overrides, and leak/regression scenarios.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
tests/test_delegation_guard.py Adds test coverage for sync/async delegation depth enforcement, lifecycle semantics, env overrides, and ContextVar leak prevention.
src/uipath/runtime/governance/delegation_guard.py Introduces the delegation guard implementation with async support, shared ContextVar tracking, and uninstall cleanup.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/uipath/runtime/governance/delegation_guard.py Outdated
Comment thread src/uipath/runtime/governance/delegation_guard.py Outdated
@aditik0303 aditik0303 force-pushed the feat/governance-evaluator branch from a019ade to 6af3c9f Compare June 16, 2026 11:03
@aditik0303 aditik0303 force-pushed the feat/governance-delegation-guard branch from aeb0d94 to 0664ff6 Compare June 16, 2026 11:03
@aditik0303 aditik0303 force-pushed the feat/governance-evaluator branch from 6af3c9f to f7cc79e Compare June 17, 2026 06:52
@aditik0303 aditik0303 force-pushed the feat/governance-delegation-guard branch from 0664ff6 to 58c7baf Compare June 17, 2026 06:54
@aditik0303 aditik0303 force-pushed the feat/governance-evaluator branch from f7cc79e to 94cea5b Compare June 17, 2026 08:35
@aditik0303 aditik0303 force-pushed the feat/governance-delegation-guard branch from 58c7baf to 20fe69c Compare June 17, 2026 08:35
@aditik0303 aditik0303 force-pushed the feat/governance-evaluator branch from 94cea5b to ce18588 Compare June 19, 2026 08:08
@aditik0303 aditik0303 force-pushed the feat/governance-delegation-guard branch from 20fe69c to d1d42d6 Compare June 19, 2026 08:08
@aditik0303 aditik0303 force-pushed the feat/governance-evaluator branch from ce18588 to e186f5f Compare June 19, 2026 11:25
@aditik0303 aditik0303 force-pushed the feat/governance-delegation-guard branch from d1d42d6 to 61e9ff7 Compare June 19, 2026 11:25
@viswa-uipath viswa-uipath force-pushed the feat/governance-evaluator branch from e186f5f to 5812bbf Compare June 24, 2026 12:44
aditik0303 and others added 3 commits June 25, 2026 12:24
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ethod is actually patched; correct _resolve_max_depth docstring (install-time)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes architecture-review §2.3 — the delegation guard monkey-patched
agent ``invoke``/``ainvoke`` methods in place via ``setattr``, naming
no framework but mutating framework-owned objects through their
private shapes. Fragile, depends on agent internals, and the runtime
layer shouldn't be reaching into objects it didn't construct.

Correct seam is the framework callback handler, which already
receives ``parent_run_id`` on every callback and can derive
delegation depth from the run tree without touching the agent. That
work lives on the LangChain side (uipath-langchain-python PR #899,
which is done) — so the runtime-side module is dead weight.

Deletions
- src/uipath/runtime/governance/delegation_guard.py (265 LOC) —
  ``install_delegation_guard`` / ``uninstall_delegation_guard``,
  the per-agent ContextVar depth tracking, the setattr-based wrap.
- tests/test_delegation_guard.py (320 LOC) — the entire test suite
  for the deleted module.

Verification
- Monorepo grep for ``delegation_guard``,
  ``install_delegation_guard``, ``uninstall_delegation_guard``,
  ``ASI-02``, ``Excessive Agency``, and
  ``UIPATH_GOVERNANCE_MAX_DELEGATION_DEPTH``: zero hits outside the
  deleted files. The module was self-contained.
- ruff clean, mypy clean (11 source files), 357 passed + 1 skipped
  (pre-existing wrapper skip).

Net diff: −585 LOC. After this PR's rebase onto #124, the branch
contains only deletions on top of the evaluator slice.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@viswa-uipath viswa-uipath force-pushed the feat/governance-delegation-guard branch from 61e9ff7 to 95cbcb2 Compare June 25, 2026 07:01
viswa-uipath added a commit that referenced this pull request Jun 25, 2026
…Runtime

Closes architecture-review §2.1 + §2.2 — the
UiPathWrappedRuntimeFactory bolted governance onto the generic
runtime-factory registry (apply_wrappers=True turned every registered
factory into a different type, breaking isinstance checks), and the
second GovernanceRuntime in governance/wrapper.py reached into
delegate._agent_definition / framework-specific private attrs through
a 10-level walk to install framework-blind callbacks. Both patterns
the doc unambiguously says to delete.

Composition belongs in the host's decorator chain, FF-gated, where
UiPathResumableRuntime already wraps the framework runtime; this
PR's wrapper machinery was an end-run around that.

Deletions
- src/uipath/runtime/governance/wrapper.py (1002 LOC) — the second
  GovernanceRuntime with _AGENT_ATTRS / _replace_agent_in_delegate /
  model-context-var introspection.
- src/uipath/runtime/wrapper.py (55 LOC) — the lazy-import dispatch
  shim that called the deleted governance_wrapper.
- tests/test_dispose_isolation.py, tests/test_wrapper.py,
  tests/test_wrapper_internals.py (~650 LOC combined) — entire test
  suites for the deleted modules.

Updates
- src/uipath/runtime/registry.py — UiPathWrappedRuntimeFactory class
  and the apply_wrappers kwarg removed from get(). The registry
  returns the registered factory unchanged; cross-cutting concerns
  (governance, audit, …) are composed by the host into the decorator
  chain, not auto-applied here.
- src/uipath/runtime/__init__.py — drop GOVERNANCE_FEATURE_FLAG /
  apply_governance_wrapper exports.
- tests/test_registry.py — strip every apply_wrappers=False kwarg
  (the kwarg is gone) and drop the wrapping-behaviour section + its
  fixtures.

Conflict resolution
The rebase onto #125's tip replayed the upstream e186f5f commit (a
cosmetic helper-import touch) into three test files that my PR
#122/#123/#124 refactors had already rewritten end-to-end. HEAD-side
resolution kept the refactored form in test_evaluator.py,
test_evaluator_operators.py, test_guardrail_compensation.py — the
incoming side referenced symbols (governance.audit,
governance.config, tests._helpers.reset_enforcement_mode) that the
post-rebase stack no longer ships.

Verification
- Monorepo grep for UiPathWrappedRuntimeFactory, apply_wrappers,
  apply_governance_wrapper, governance_wrapper, and the deleted
  module import paths: zero hits.
- ruff clean, mypy clean (45 source files), 357 passed + 1 skipped.

Net diff on top of #125's tip: −2005 / +38 LOC = −1967 net.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants