Skip to content

feat(policy): expose decisions in invoke results#492

Open
Subhasish1802 wants to merge 2 commits into
cuga-project:mainfrom
Subhasish1802:feature/policy-observability
Open

feat(policy): expose decisions in invoke results#492
Subhasish1802 wants to merge 2 commits into
cuga-project:mainfrom
Subhasish1802:feature/policy-observability

Conversation

@Subhasish1802

@Subhasish1802 Subhasish1802 commented Jul 14, 2026

Copy link
Copy Markdown

Feature

Closes #491
Related to #382

Summary

  • Adds typed policy decision records with stage and outcome information.
  • Exposes policy decisions through InvokeResult for normal and HITL SDK paths.
  • Propagates decisions through CugaLite, supervisor, approval, and delegated-agent flows.
  • Preserves backward compatibility by defaulting policy_decisions to an empty list.
  • Keeps structured ToolGuard observability outside this change as a follow-up.

Testing

  • Tested locally; tests pass
  • uv run ruff format
  • uv run ruff check
  • Focused policy, SDK, approval, and delegation suite: 27 passed

Summary by CodeRabbit

  • New Features
    • Added structured policy_decisions to agent and supervisor invocation results, including policy identity, stage, and outcome (with optional tool/agent context).
    • Policy decision records are now captured during enactment, tool approval (approval-required/approved/denied), and delegated workflows, and are serialized into updates.
    • Public policy decision types are now re-exported for easier SDK access.
  • Bug Fixes
    • Prevented policy decisions from carrying over between separate requests (including non-resume invocations).

Signed-off-by: Subhasish1802 <subhasish1802@gmail.com>
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4d66a9e5-04c9-4110-a889-aab62f44612e

📥 Commits

Reviewing files that changed from the base of the PR and between d0d8d4c and 6d7c04e.

📒 Files selected for processing (3)
  • src/cuga/backend/cuga_graph/nodes/cuga_lite/adapter/prepare_node.py
  • src/cuga/backend/cuga_graph/nodes/cuga_supervisor/nodes/prepare_agents_and_prompt.py
  • src/cuga/backend/cuga_graph/policy/tests/test_policy_observability.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/cuga/backend/cuga_graph/nodes/cuga_supervisor/nodes/prepare_agents_and_prompt.py
  • src/cuga/backend/cuga_graph/nodes/cuga_lite/adapter/prepare_node.py
  • src/cuga/backend/cuga_graph/policy/tests/test_policy_observability.py

📝 Walkthrough

Walkthrough

The PR adds typed policy decision models and observability helpers, records decisions across policy enactment, HITL approval, CugaLite, and supervisor delegation flows, propagates them through graph state, and exposes them on SDK InvokeResult.

Changes

Policy decision observability

Layer / File(s) Summary
Decision contracts and sanitization
src/cuga/backend/cuga_graph/policy/models.py, src/cuga/backend/cuga_graph/policy/observability.py, src/cuga/backend/cuga_graph/state/agent_state.py, src/cuga/backend/cuga_graph/nodes/cuga_lite/cuga_lite_graph.py, src/cuga/backend/cuga_graph/policy/__init__.py, src/cuga/__init__.py, src/cuga/backend/cuga_graph/policy/tests/*
Adds typed decision stages, outcomes, and public decision records; provides metadata conversion, deduplication, and JSON serialization; adds invocation state fields, lazy policy exports, package exports, and unit coverage.
Enactment and approval lifecycle recording
src/cuga/backend/cuga_graph/policy/enactment.py, src/cuga/backend/cuga_graph/nodes/cuga_agent_core/policy/tool_approval_handler.py, src/cuga/backend/cuga_graph/nodes/cuga_lite/cuga_lite_node.py, src/cuga/backend/cuga_graph/nodes/cuga_supervisor/cuga_supervisor_node.py, src/cuga/backend/cuga_graph/nodes/cuga_agent_core/tests/policy/*
Records policy matches and approval lifecycle outcomes, including serialized decisions in blocking, interruption, approval, and denial command updates.
Graph and delegation propagation
src/cuga/backend/cuga_graph/nodes/cuga_lite/adapter/prepare_node.py, src/cuga/backend/cuga_graph/nodes/cuga_supervisor/nodes/*, src/cuga/backend/cuga_graph/nodes/cuga_supervisor/supervisor_graph_adapter.py, src/cuga/backend/cuga_graph/nodes/cuga_supervisor/tests/test_delegation_recording.py
Carries policy decisions through CugaLite and supervisor updates, and normalizes delegated decisions with the delegating agent name.
SDK result exposure and validation
src/cuga/sdk.py, src/cuga/sdk_core/tests/test_invoke_result_policy_decisions.py
Adds InvokeResult.policy_decisions, populates it from agent and supervisor graph results, resets it for new requests, and tests defaulting and JSON parsing.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SDKClient
  participant CugaAgent
  participant PolicyEnactment
  participant ToolApprovalHandler
  participant AgentState
  SDKClient->>CugaAgent: invoke request
  CugaAgent->>PolicyEnactment: check and enact policies
  PolicyEnactment->>AgentState: append policy decisions
  CugaAgent->>ToolApprovalHandler: process tool approval
  ToolApprovalHandler->>AgentState: append approval outcome
  AgentState-->>CugaAgent: serialized policy decisions
  CugaAgent-->>SDKClient: InvokeResult with policy_decisions
Loading

Possibly related PRs

Suggested labels: readability: good, complexity: high

Suggested reviewers: offerakrabi, sami-marreed

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 52.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the main change: exposing policy decisions in invoke results.
Linked Issues check ✅ Passed The PR adds typed policy_decisions to InvokeResult and propagates them through the required SDK and HITL execution paths.
Out of Scope Changes check ✅ Passed The changes stay focused on policy decision tracking, serialization, and SDK exposure without unrelated feature work.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Subhasish1802
Subhasish1802 marked this pull request as ready for review July 14, 2026 15:51
@coderabbitai coderabbitai Bot added complexity: medium Moderate scope — multiple files or non-trivial logic readability: fair Partial context or goal not obvious on first read labels Jul 14, 2026
@Subhasish1802

Copy link
Copy Markdown
Author

Hi @sami-marreed , the external fork workflows are currently awaiting maintainer approval. Could you approve them when convenient? Thanks!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/cuga/sdk.py (1)

2728-2734: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reset policy_decisions to prevent cross-turn leakage during streaming.

The normal invocation path (in CugaAgent.invoke) explicitly resets policy_decisions to an empty list on new turns so the results strictly reflect the current request. However, CugaAgent.stream omits this reset when creating the initial state dictionary.

When resuming or starting a new turn on an existing thread, LangGraph will merge this initial_state with the checkpoint. Without an explicit reset, the agent will inherit and leak policy decisions from previous turns.

🐛 Proposed fix to align state construction
         # Create initial state for HITL wrapper graph (uses AgentState format)
         initial_state = {
             "chat_messages": messages,
             "thread_id": thread_id,
             "input": messages[-1].content if messages else "",
             "url": "",  # Required by AgentState (used for web navigation, empty for SDK)
+            "policy_decisions": [],
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cuga/sdk.py` around lines 2728 - 2734, Update the initial_state
construction in CugaAgent.stream to include policy_decisions initialized to an
empty list, matching the reset performed by CugaAgent.invoke. Ensure this
explicit value overwrites checkpointed decisions when starting or resuming a
turn.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/cuga/backend/cuga_graph/nodes/cuga_lite/adapter/prepare_node.py`:
- Around line 648-650: Update the policy_decisions serialization in the relevant
node to call serialize_policy_decisions instead of invoking decision.model_dump
directly, preserving safe handling of checkpoint-restored dictionaries and model
instances. Import serialize_policy_decisions from
cuga.backend.cuga_graph.policy.observability if needed.

---

Outside diff comments:
In `@src/cuga/sdk.py`:
- Around line 2728-2734: Update the initial_state construction in
CugaAgent.stream to include policy_decisions initialized to an empty list,
matching the reset performed by CugaAgent.invoke. Ensure this explicit value
overwrites checkpointed decisions when starting or resuming a turn.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4ec0bbb6-d225-4ef3-b9a4-5b425eb2719a

📥 Commits

Reviewing files that changed from the base of the PR and between f3511e9 and d0d8d4c.

📒 Files selected for processing (20)
  • src/cuga/__init__.py
  • src/cuga/backend/cuga_graph/nodes/cuga_agent_core/policy/tool_approval_handler.py
  • src/cuga/backend/cuga_graph/nodes/cuga_agent_core/tests/policy/test_policy_enactment_adapter.py
  • src/cuga/backend/cuga_graph/nodes/cuga_agent_core/tests/policy/test_tool_approval_adapter.py
  • src/cuga/backend/cuga_graph/nodes/cuga_lite/adapter/prepare_node.py
  • src/cuga/backend/cuga_graph/nodes/cuga_lite/cuga_lite_graph.py
  • src/cuga/backend/cuga_graph/nodes/cuga_lite/cuga_lite_node.py
  • src/cuga/backend/cuga_graph/nodes/cuga_supervisor/cuga_supervisor_node.py
  • src/cuga/backend/cuga_graph/nodes/cuga_supervisor/nodes/execute_agent_tool.py
  • src/cuga/backend/cuga_graph/nodes/cuga_supervisor/nodes/prepare_agents_and_prompt.py
  • src/cuga/backend/cuga_graph/nodes/cuga_supervisor/supervisor_graph_adapter.py
  • src/cuga/backend/cuga_graph/nodes/cuga_supervisor/tests/test_delegation_recording.py
  • src/cuga/backend/cuga_graph/policy/__init__.py
  • src/cuga/backend/cuga_graph/policy/enactment.py
  • src/cuga/backend/cuga_graph/policy/models.py
  • src/cuga/backend/cuga_graph/policy/observability.py
  • src/cuga/backend/cuga_graph/policy/tests/test_policy_observability.py
  • src/cuga/backend/cuga_graph/state/agent_state.py
  • src/cuga/sdk.py
  • src/cuga/sdk_core/tests/test_invoke_result_policy_decisions.py

Comment thread src/cuga/backend/cuga_graph/nodes/cuga_lite/adapter/prepare_node.py Outdated
Signed-off-by: Subhasish1802 <subhasish1802@gmail.com>
@coderabbitai coderabbitai Bot added complexity: high Large or risky change — needs careful review readability: good Clear PR goal and description; easy to review and removed readability: fair Partial context or goal not obvious on first read complexity: medium Moderate scope — multiple files or non-trivial logic labels Jul 14, 2026
@Subhasish1802

Copy link
Copy Markdown
Author

Hi @sami-marreed and @offerakrabi, gentle follow-up on PR #492. The three external-fork workflows are still awaiting maintainer approval.

The CodeRabbit finding has been addressed, Ruff passes, and the expanded local test suite reports 49 passed. When convenient, could you approve the pending workflows so CI can run?

If you have any questions about the implementation, please contact me at subhasish1802@gmail.com.

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

complexity: high Large or risky change — needs careful review readability: good Clear PR goal and description; easy to review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Design]: Expose policy decisions through InvokeResult

1 participant