Skip to content

[codex] Consume runtime approval decisions - #95

Merged
delete-cloud merged 6 commits into
mainfrom
approval-decision-pr6
May 3, 2026
Merged

delete-cloud merged 6 commits into
mainfrom
approval-decision-pr6

Conversation

@delete-cloud

@delete-cloud delete-cloud commented May 3, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • add a coding_agent.approval runtime-message consumer for product-owned approval_decision messages
  • give HTTP sessions a session-local runtime message bus plus an approval-decision cursor independent of the agentkit pipeline cursor
  • route SessionManager.submit_approval() through publish/consume of approval_decision before applying the decision to ApprovalCoordinator
  • share the session runtime bus with PipelineContext for HTTP session turns while keeping agentkit from interpreting approval decisions
  • document the approval decision payload shape and product-side routing path

Verification

  • uv run pytest tests/coding_agent/test_approval_runtime_messages.py -q (red first: missing coding_agent.approval.runtime_messages; green after implementation)
  • uv run pytest tests/coding_agent/test_approval_runtime_messages.py tests/coding_agent/test_pipeline_adapter.py tests/coding_agent/plugins/test_approval.py tests/agentkit/runtime/test_pipeline.py tests/agentkit/runtime/test_runtime_messages.py -q (90 passed)
  • uv run pytest tests/agentkit/runtime/test_pipeline.py tests/agentkit/runtime/test_runtime_messages.py -q (35 passed)
  • uv run pytest tests/coding_agent/ -q -k "not TestGoldenFixtures and not test_http_server_legacy_session_shims_removed" (460 passed, 5 deselected)
  • uv run basedpyright src/coding_agent/approval/runtime_messages.py src/coding_agent/approval/__init__.py src/coding_agent/ui/session_manager.py src/agentkit/runtime/messages.py tests/coding_agent/test_approval_runtime_messages.py --level error (0 errors)
  • uv run python -m py_compile src/coding_agent/approval/runtime_messages.py src/coding_agent/approval/__init__.py src/coding_agent/ui/session_manager.py src/agentkit/runtime/messages.py tests/coding_agent/test_approval_runtime_messages.py
  • git diff --check

Known Existing Failures

  • Full tests/coding_agent/ -q still excludes the three TestGoldenFixtures cases because coding-agent/data/eval/golden/parent-child-subagent-001.{jsonl,yaml} is absent.
  • Full tests/coding_agent/ -q still excludes test_http_server_legacy_session_shims_removed because current http_server.py still contains legacy session shims outside this PR.

Summary by CodeRabbit

  • Documentation

    • Clarified architecture docs on session-local approval decisions, routing, payload fields, first-write-wins semantics, and independent cursors.
  • New Features

    • Sessions now include an in-memory runtime message bus and per-session approval-decision cursor to handle approvals.
  • Refactor

    • Approval submissions are routed through the session-local runtime bus with deterministic IDs and first-write-wins behavior.
  • Tests

    • Added coverage for decision consumption, validation/skips, duplicate handling, retries, and session approval flows.

@coderabbitai

coderabbitai Bot commented May 3, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@delete-cloud has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 27 minutes and 1 second before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 41c5e2aa-7183-4515-b676-f8878f495a4f

📥 Commits

Reviewing files that changed from the base of the PR and between b1bc742 and 850c5b0.

📒 Files selected for processing (5)
  • coding-agent/src/coding_agent/approval/runtime_messages.py
  • coding-agent/src/coding_agent/ui/http_server.py
  • coding-agent/src/coding_agent/ui/session_manager.py
  • coding-agent/tests/coding_agent/test_approval_runtime_messages.py
  • coding-agent/tests/ui/test_http_server.py
📝 Walkthrough

Walkthrough

Adds session-local runtime-message handling for approval decisions: a typed consumer, deterministic decision publishing with first-write-wins semantics, a DuplicateRuntimeMessageError, Session wiring for a runtime bus and approval cursor, and tests plus docs describing the flow and payload schema.

Changes

Approval-Decision Runtime Messaging

Layer / File(s) Summary
Documentation
coding-agent/docs/AGENTKIT-ARCHITECTURE.md, coding-agent/docs/CODING-AGENT-ARCHITECTURE.md
Documents session-local runtime bus for approval_decision messages, payload schema (session_id, request_id, approved, optional feedback, scope), independent approval cursor, first-write-wins semantics, in-memory retention.
Runtime types / errors
coding-agent/src/agentkit/runtime/messages.py, coding-agent/src/agentkit/runtime/__init__.py
Module docstring documents approval_decision payload; added DuplicateRuntimeMessageError and re-exported it from agentkit.runtime; InMemoryRuntimeMessageBus now raises DuplicateRuntimeMessageError on duplicate message_id.
Core Consumer Implementation
coding-agent/src/coding_agent/approval/runtime_messages.py
Added ApprovalDecisionConsumer, ApprovalDecisionConsumptionResult, and approval_response_from_runtime_payload; consumer validates payloads, enforces session targeting, applies valid ApprovalResponse via ApprovalCoordinator.respond, and returns updated cursor with applied/skipped IDs.
Public API Export
coding-agent/src/coding_agent/approval/__init__.py
Exports ApprovalDecisionConsumer, ApprovalDecisionConsumptionResult, approval_response_from_runtime_payload, and expands approval package exports (policy/store symbols).
Session wiring & submit flow
coding-agent/src/coding_agent/ui/session_manager.py
Session gains runtime_message_bus and approval_decision_cursor; run_agent wires session bus into runtime context; submit_approval_response publishes deterministic APPROVAL_DECISION to session-local bus (handles DuplicateRuntimeMessageError by reusing first-write), then consumes decisions via ApprovalDecisionConsumer to update cursor, pending approvals, and signals; submit_approval now delegates to submit_approval_response.
HTTP handler
coding-agent/src/coding_agent/ui/http_server.py
approve_request updated to call submit_approval_response and derive the returned decision from the applied ApprovalResponse.
Tests
coding-agent/tests/coding_agent/test_approval_runtime_messages.py, coding-agent/tests/agentkit/runtime/test_runtime_messages.py, coding-agent/tests/ui/test_http_server.py
Added tests for consumer apply/skip cases, cross-session rejection, cursor advancement; session-manager submit flow tests (publish, first-write-wins, duplicate acceptance, pending-projection advancement); runtime bus duplicate-publish now asserts DuplicateRuntimeMessageError; HTTP endpoint tests updated to exercise retry/first-write-wins behavior and polling adjustment.

Sequence Diagram

sequenceDiagram
    actor User
    participant SessionManager
    participant SessionRuntimeBus as "Session Runtime Bus"
    participant ApprovalDecisionConsumer as "ApprovalDecisionConsumer"
    participant ApprovalCoordinator as "ApprovalCoordinator"

    User ->> SessionManager: submit_approval_response(session_id, request_id, approved, feedback, scope)
    SessionManager ->> SessionRuntimeBus: publish(APPROVAL_DECISION with deterministic message_id)
    SessionManager ->> ApprovalDecisionConsumer: consume(bus, cursor)
    ApprovalDecisionConsumer ->> SessionRuntimeBus: read messages after cursor
    ApprovalDecisionConsumer ->> ApprovalDecisionConsumer: validate payload (request_id, approved, feedback?, scope, session match)
    alt payload valid & session matches
        ApprovalDecisionConsumer ->> ApprovalCoordinator: respond(ApprovalResponse)
        ApprovalCoordinator -->> ApprovalDecisionConsumer: applied / acknowledged
        ApprovalDecisionConsumer -->> SessionManager: ApprovalDecisionConsumptionResult(applied_request_ids=[...])
        SessionManager ->> SessionManager: update cursor, pending_approval, set approval_event
    else invalid / cross-session / coordinator rejects
        ApprovalDecisionConsumer -->> SessionManager: ApprovalDecisionConsumptionResult(skipped_message_ids=[...])
        SessionManager ->> SessionManager: advance cursor only
    end
    SessionManager ->> User: return ApprovalResponse or None (success if applied_request_ids contains request_id)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped a message through the lane,
Session bus hummed like springtime rain.
Decisions stamped with first-write cheer,
Cursors advance and signals clear.
Carrots, logs, and approvals near.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% 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 '[codex] Consume runtime approval decisions' directly and clearly describes the main change: implementing consumption of runtime approval decision messages in the coding agent.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch approval-decision-pr6

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
Review rate limit: 0/1 reviews remaining, refill in 27 minutes and 1 second.

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

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@coding-agent/src/coding_agent/approval/runtime_messages.py`:
- Around line 116-119: The membership check for scope_value against
_APPROVAL_SCOPES can raise TypeError for unhashable values (e.g.,
typed_payload["scope"] == []); update the logic in the block that reads
typed_payload, scope_value and uses logger.warning/message_id so it first guards
the value (e.g., ensure scope_value is a simple hashable like str or wrap the
membership test in a try/except TypeError) and treat any TypeError or non-string
as invalid by logging via logger.warning("approval_decision %r has invalid
scope", message_id) and returning None; keep the same log/message_id and
_APPROVAL_SCOPES identifiers so only the membership check behavior changes.

In `@coding-agent/src/coding_agent/ui/session_manager.py`:
- Around line 1452-1465: The publish can raise ValueError on duplicate message
during retries; make the approval publish idempotent by catching ValueError
around the session.runtime_message_bus.publish(...) call (the RuntimeMessage
with message_id f"approval_decision:{session_id}:{request_id}") and treating
that specific duplicate-error as success (i.e., log/debug and continue) before
calling self._consume_approval_decisions_for_session(session); ensure you only
suppress the duplicate-existence error and re-raise other exceptions so the
read-after-write path remains intact.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f2d4794a-8989-4705-846f-7dfad7b62b97

📥 Commits

Reviewing files that changed from the base of the PR and between 74ece87 and 6d45c16.

📒 Files selected for processing (7)
  • coding-agent/docs/AGENTKIT-ARCHITECTURE.md
  • coding-agent/docs/CODING-AGENT-ARCHITECTURE.md
  • coding-agent/src/agentkit/runtime/messages.py
  • coding-agent/src/coding_agent/approval/__init__.py
  • coding-agent/src/coding_agent/approval/runtime_messages.py
  • coding-agent/src/coding_agent/ui/session_manager.py
  • coding-agent/tests/coding_agent/test_approval_runtime_messages.py

@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)
coding-agent/src/coding_agent/ui/http_server.py (1)

748-762: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Return 500 for runtime-message failures instead of echoing them as bad input.

submit_approval_response() can now fail with server-side runtime-message errors, but this blanket except Exception turns them into 400 responses and sends the internal message text back to the client. That misclassifies a backend inconsistency as a caller mistake and can leak internal session/request identifiers. Catch only expected validation errors here, and log unexpected failures as 500s.

Proposed fix
-    except Exception as exc:
-        raise HTTPException(status_code=400, detail=str(exc)) from exc
+    except (ValueError, TypeError) as exc:
+        raise HTTPException(status_code=400, detail=str(exc)) from exc
+    except Exception as exc:
+        logger.exception(
+            "Unexpected approval submission failure for session %s",
+            session_id,
+        )
+        raise HTTPException(status_code=500, detail="Internal server error") from exc
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@coding-agent/src/coding_agent/ui/http_server.py` around lines 748 - 762, The
except block around the call to session_manager.submit_approval_response should
stop turning all exceptions into 400s and avoid echoing internal error text;
catch only expected validation/known client errors (e.g., whichever validation
exception your codebase uses) and re-raise them as
HTTPException(status_code=400), but for any other unexpected Exception from
submit_approval_response or session_manager, log the exception and raise
HTTPException(status_code=500, detail="Internal server error") instead; keep
existing handling for SessionOwnershipConflictError and HTTPException, and
reference submit_approval_response, session_manager,
SessionOwnershipConflictError and approval_response when locating the code to
change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@coding-agent/src/coding_agent/ui/session_manager.py`:
- Around line 1405-1414: The helper currently advances
session.approval_decision_cursor immediately from the
ApprovalDecisionConsumer.consume() result even when decisions were skipped
because their target request doesn't exist yet; change the logic so you only
advance the cursor and set session.pending_approval / session.approval_event
when the consumer actually applied a decision to an existing request.
Concretely: inspect the consume() outcome (e.g., result.applied_request_ids and
any per-decision status returned by ApprovalDecisionConsumer.consume()) and
distinguish "request not found yet" from "already applied"; if a decision was
skipped due to "request not found", do not update
session.approval_decision_cursor or return success—keep the cursor unchanged so
the decision remains replayable until ApprovalCoordinator.add_request(...)
attaches the request, and only then update session.approval_decision_cursor and
set session.pending_approval = session.approval_coordinator.projection() /
session.approval_event when a real application occurred; apply the same change
to the analogous block around the branch handling lines 1468-1482.

---

Outside diff comments:
In `@coding-agent/src/coding_agent/ui/http_server.py`:
- Around line 748-762: The except block around the call to
session_manager.submit_approval_response should stop turning all exceptions into
400s and avoid echoing internal error text; catch only expected validation/known
client errors (e.g., whichever validation exception your codebase uses) and
re-raise them as HTTPException(status_code=400), but for any other unexpected
Exception from submit_approval_response or session_manager, log the exception
and raise HTTPException(status_code=500, detail="Internal server error")
instead; keep existing handling for SessionOwnershipConflictError and
HTTPException, and reference submit_approval_response, session_manager,
SessionOwnershipConflictError and approval_response when locating the code to
change.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 35665b11-ec44-4d1f-94cf-e528c7a7076f

📥 Commits

Reviewing files that changed from the base of the PR and between 965a12b and b1bc742.

📒 Files selected for processing (5)
  • coding-agent/src/coding_agent/approval/__init__.py
  • coding-agent/src/coding_agent/approval/runtime_messages.py
  • coding-agent/src/coding_agent/ui/http_server.py
  • coding-agent/src/coding_agent/ui/session_manager.py
  • coding-agent/tests/ui/test_http_server.py
✅ Files skipped from review due to trivial changes (1)
  • coding-agent/src/coding_agent/approval/init.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • coding-agent/src/coding_agent/approval/runtime_messages.py

@delete-cloud
delete-cloud merged commit 4f729fe into main May 3, 2026
1 check passed
@delete-cloud
delete-cloud deleted the approval-decision-pr6 branch May 10, 2026 03:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant