Skip to content

Three things a first-time user hits, found by being one - #211

Merged
rohanrkamath merged 1 commit into
mainfrom
fix/gateway-approval-message
Sep 15, 2026
Merged

rohanrkamath merged 1 commit into
mainfrom
fix/gateway-approval-message

Conversation

@arpanghoshal

@arpanghoshal arpanghoshal commented Sep 14, 2026

Copy link
Copy Markdown
Member

None of these came from the suite. I installed the published wheel into an empty venv, built a real LangGraph agent and a real MCP server, and used them the way the READMEs say to.

1. The gateway told MCP clients to call a Python API

_awaiting relayed str(ApprovalRequired) verbatim, and that exception carries the decorator's wording:

mcp.ops.delete_namespace requires approval: run 'ctrlrun approve apr_…',
then retry inside ctrlrun.with_approval('apr_…')

with_approval is a Python context manager. This is the one path where the caller may be in any language, and is frequently a model reading the error as text. We were pointing it at something it cannot reach.

docs/mcp/gateway-in-5-minutes.mdx already documents the real next step, so the gateway now says it:

mcp.ops.delete_namespace requires approval: a human runs
'ctrlrun approve apr_…', then this same call runs

Verified against a live gateway in front of a real MCP server, not only in a test. The test asserts with_approval is absent, because a message merely mentioning the retry would pass a positive-only check while still sending a Go client looking for a Python API. Run against main it fails with the old string.

2. Both adapter READMEs fail on copy-paste

Their examples show identity=... as an ellipsis placeholder. Drop it, as anyone adapting the snippet does, and the first call dies:

ActionDenied: stripe.refund: no principal is available; wrap the call in
'with ctrlrun.context(agent=...)', or install an identity provider that answers

The error names both fixes, which is exactly why this is a documentation defect and not a code one. Both READMEs now state the requirement, show that error, and say why it is fail-closed: who is acting is an authorization input, and a library that guessed it would be inventing the one field a grant is matched against.

3. The README promised a browser demo that does not exist

Three places: "Run it in your browser with nothing installed", "a browser demo that runs with no install", and a "Try it in your browser" link. /try-it redirects to the marketing page. I checked it for pyodide, an editor, anything runnable: there is none. It was the first link in the opening section, so on a Show HN it is among the first things clicked.

Removed, with the local one-liner in its place, which is true and about as fast. The /try-it redirect stays so existing bookmarks do not 404 — what was wrong was the promise, not the route.

What else I exercised, which was all correct

  • 8 OS processes racing one refund → exactly 1 provider call, 7 DuplicateEffect
  • kill -9 mid-flight → lease expires, blind retry refused with AmbiguousEffect naming the exact ctrlrun resolve command; resolve then permits the retry
  • A real LangGraph agent, human-approval round trip, 0 provider calls before approval
  • A mutated resume (human approved €5,000, resume claimed €9,000) → ApprovalMismatch, nothing ran
  • Observe mode, float amounts, missing policy, malformed YAML, unknown action → every failure fail-closed and self-explaining
  • PyPI install on 3.11, 3.12, 3.13, 3.14

Gate

All checks passed!            (ruff, ruff format, mypy --strict: 54 files)
4592 passed, 6 warnings in 218.14s

Pairing

Docs are fix/gateway-approval-message: the documented -41002 example was stale in the same way.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation

    • Updated setup guidance to use ctrlrun demo locally instead of the browser demo.
    • Documented that protected calls require a principal and explained production and development options.
    • Clarified that calls without a principal are denied before policy evaluation.
  • Bug Fixes

    • Improved approval-required messages with clear instructions to run ctrlrun approve <request_id> and retry the same call.
    • Included relevant approval details, such as the request ID, in client-facing responses.

Not from the suite. I installed the published wheel into an empty venv, built a
real LangGraph agent and a real MCP server, and used them the way the READMEs
say to.

**1. The gateway told MCP clients to call a Python API.** `_awaiting` relayed
`str(ApprovalRequired)` verbatim, and that exception carries the decorator's
wording: *"run `ctrlrun approve …`, then retry inside
`ctrlrun.with_approval(…)`"*. `with_approval` is a context manager. On the one
path where the caller may be in any language, and is often a model reading the
error as text, we were pointing at something it cannot reach.

`gateway-in-5-minutes.mdx` already documents the real next step, and the gateway
now says it: a human approves and **this same call runs**, because the approval
is bound to the action's hash. Verified against a live gateway, not only in a
test. The test asserts `with_approval` is ABSENT, because a message merely
mentioning the retry would pass a positive-only check while still sending a Go
client looking for Python.

**2. Both adapter READMEs fail on copy-paste.** Their examples show
`identity=...` as an ellipsis, which is easy to drop, and then the first call
dies with `no principal is available`. The error names both fixes, which is why
this is a documentation defect and not a code one. Both READMEs now say a
principal is required, show the error, and say why it is fail-closed: who is
acting is an authorization input, and guessing it would invent the one field a
grant is matched against.

**3. The README promised a browser demo that does not exist.** Three places:
*"Run it in your browser with nothing installed"*, *"a browser demo that runs
with no install"*, and a "Try it in your browser" link. `/try-it` redirects to
the marketing page; there is no pyodide, no editor, nothing to run. It was the
first link in the opening section. Removed, and the local one-liner put in its
place, which is true and about as fast.

The `/try-it` redirect stays so existing bookmarks do not 404. What was wrong
was the promise, not the route.

Signed-off-by: arpan <contact@arpanghoshal.com>
@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 877bc3ac-b954-4445-902c-fd3b64163b90

📥 Commits

Reviewing files that changed from the base of the PR and between 4634fbd and c34cf3d.

📒 Files selected for processing (5)
  • README.md
  • adapters/langgraph/README.md
  • adapters/openai-agents/README.md
  • src/ctrlrun/gateway/server.py
  • tests/test_gateway_server.py

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The documentation now describes local demo execution and principal requirements. Gateway approval errors now give MCP clients explicit approval and retry instructions. A gateway test verifies the revised message and approval request identifier.

Changes

Client Guidance

Layer / File(s) Summary
Documentation guidance
README.md, adapters/langgraph/README.md, adapters/openai-agents/README.md
The documentation replaces the browser demo with ctrlrun demo instructions and describes principal requirements for protected calls.
Approval response guidance
src/ctrlrun/gateway/server.py, tests/test_gateway_server.py
The gateway approval response instructs a human to run ctrlrun approve <request_id> and retry the same call. The test verifies the message contents and request ID.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to c34cf

The client guidance and MCP approval flow match the supported repository behavior, so no merge-blocking risk remains.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title relates to the pull request because the changes address three issues found by first-time users. It is broad and does not identify the approval, identity, and demo documentation changes, but …
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 2 files. (3 skipped: 3 u…
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/gateway-approval-message

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.

@rohanrkamath
rohanrkamath merged commit d07d7c8 into main Sep 15, 2026
16 checks passed
@rohanrkamath
rohanrkamath deleted the fix/gateway-approval-message branch September 15, 2026 00:10
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.

2 participants