Skip to content

fix(composio): let a provider reshape supersede the backend markdown rendering - #5323

Open
yh928 wants to merge 7 commits into
tinyhumansai:mainfrom
yh928:fix/gmail-thread-list-body
Open

fix(composio): let a provider reshape supersede the backend markdown rendering#5323
yh928 wants to merge 7 commits into
tinyhumansai:mainfrom
yh928:fix/gmail-thread-list-body

Conversation

@yh928

@yh928 yh928 commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Stacked on #5259, which owns the post_process_action_result call sites in both dispatch paths. Review that one first; the diff here is the two 11-line hooks plus the provider surface.

Summary

  • reshape_list_threads rewrites GMAIL_LIST_THREADS into a usable thread summary, and nothing read it. This makes it reach the model.
  • New ComposioProvider::reshape_supersedes_markdown(slug) lets a provider declare that its rewrite replaces the backend's rendering; both dispatch paths clear markdown_formatted when it answers true.
  • Deliberately per action, not per toolkit.

Problem

Both Composio dispatch paths build the model-facing body the same way: prefer the backend's markdownFormatted, fall back to the JSON envelope only when it is absent or the call failed. post_process_action_result receives only data, so a provider cannot clear that field. The reshape runs, rewrites data, and the model is handed the backend rendering instead.

For a thread list the rendering is the whole problem. Captured verbatim from a live verbose response:

**Gmail Threads** — 2 returned
- `19fbd08f6a3e635b`
- `19fbc77215064e91`

_nextPageToken: 16343739521893202356_
_resultSizeEstimate: 201_

Bare ids. The subjects, senders, dates, labels, and snippets are all present in the payload behind that list, and all discarded. Live, a sub-agent searching for a mail that does exist got the ids, had nothing to recognise the thread by, and reported it could not be found.

Solution

A provider answers reshape_supersedes_markdown(slug); the two call sites clear resp.markdown_formatted when it does. Default false, so a provider that has not thought about it keeps today's behaviour.

Per action, not per toolkit, because within Gmail the answer differs. GMAIL_FETCH_EMAILS's reshape reads markdownFormatted for the message body (extract_markdown_body) — it is already URL-shortened and footer-stripped by the backend, and clearing it there would throw the body away rather than reveal it. Only GMAIL_LIST_THREADS returns true.

The trait doc states the trap directly, because it is not visible from the reshape's own file: a reshape of an action the backend also renders is invisible unless the provider says otherwise.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case)
  • Diff coverage ≥ 80% — both branches of the new predicate are covered, plus the reshape itself
  • N/A: behaviour-only change, no feature row added/removed/renamed — Coverage matrix updated
  • No new external network dependencies introduced
  • N/A: no release-cut surface touched — Manual smoke checklist updated
  • Linked issue closed via Closes #NNN in the ## Related section

Testing

  • the_thread_list_reshape_supersedes_the_backend_rendering — including the case-insensitive form, since the model's casing varies and the action does not.
  • the_message_fetch_reshape_does_not — the failure case that matters: GMAIL_FETCH_EMAILS, GMAIL_FETCH_MESSAGE_BY_THREAD_ID, and a write action must all stay false, or the body is thrown away.

Impact

  • Changes what the model sees for GMAIL_LIST_THREADS only. Every other action, and every other toolkit, is byte-identical.
  • The reshaped summary is smaller than the raw envelope it replaces (it drops payload), so context cost goes down, not up.

Related

Closes #5319

Summary by CodeRabbit

  • New Features

    • Added provider-specific response formatting for external actions.
    • Gmail thread lists now show concise details, including subject, sender, date, labels, snippets, message counts, and pagination.
    • Gmail action matching now works regardless of letter casing.
  • Bug Fixes

    • Write and administrative actions now require approval, while read-only actions proceed without prompting.
    • Improved handling of missing or invalid action details.
    • Preserved raw HTML behavior when explicitly requested.
    • Prevented duplicate backend formatting when provider-specific output takes precedence.

@yh928
yh928 requested a review from a team August 2, 2026 12:40
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Composio now gates mutating or unknown actions, preserves execution arguments, and applies provider-specific response reshaping. Gmail thread-list results become compact summaries, with backend Markdown replaced only when appropriate.

Changes

Composio action flow

Layer / File(s) Summary
External-effect classification and approval gating
src/openhuman/integrations/composio/tools.rs, src/openhuman/integrations/composio/action_tool.rs, src/openhuman/integrations/composio/tools_tests.rs
Write and admin actions require approval. Read actions bypass prompting. Missing or blank slugs use gated behavior.
Provider response reshaping integration
src/openhuman/memory/sync/composio/providers/traits.rs, src/openhuman/memory/sync/composio/providers/registry.rs, src/openhuman/memory/sync/composio/providers/mod.rs, src/openhuman/integrations/composio/tools.rs, src/openhuman/integrations/composio/action_tool.rs
Successful responses resolve a provider and receive post-processing. Providers can suppress backend Markdown.
Gmail thread response transformation
src/openhuman/memory/sync/composio/providers/gmail/post_process.rs, src/openhuman/memory/sync/composio/providers/gmail/provider.rs
GMAIL_LIST_THREADS responses expose thread metadata, snippets, labels, counts, and pagination without raw message payloads. Matching is case-insensitive. Raw HTML mode preserves backend Markdown.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ComposioActionTool
  participant ComposioExecuteTool
  participant ProviderRegistry
  participant GmailProvider
  ComposioActionTool->>ComposioExecuteTool: execute Composio action
  ComposioExecuteTool->>ProviderRegistry: resolve provider for successful slug
  ProviderRegistry->>GmailProvider: select Gmail provider
  ComposioExecuteTool->>GmailProvider: reshape response
  GmailProvider-->>ComposioExecuteTool: thread summaries and Markdown decision
  ComposioExecuteTool-->>ComposioActionTool: publish model-facing result
Loading

Possibly related PRs

Suggested labels: rust-core, bug

Poem

A rabbit found threads in a haystack of mail,
With subjects and senders and snippets in trail.
Write actions wait for approval’s light,
Read actions pass without a fight.
Slim summaries now hop into sight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR also changes Composio approval gating for write, admin, malformed, and missing-slug actions, which is unrelated to the linked issue. Move the approval-gating changes to a separate PR or link an issue that defines those requirements.
✅ 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 clearly describes the primary change: provider reshaping can supersede backend Markdown rendering.
Linked Issues check ✅ Passed The changes implement per-action reshape precedence across both dispatch paths and preserve Markdown for actions such as GMAIL_FETCH_EMAILS [#5319].
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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.

@coderabbitai coderabbitai Bot added bug rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. labels Aug 2, 2026
@greptile-apps

greptile-apps Bot commented Aug 2, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes a real-world agent failure: GMAIL_LIST_THREADS was being rendered by the Composio backend as a bare list of thread hex IDs, discarding the subjects, senders, dates, and snippets that the same payload carries. The new reshape_list_threads lifts those fields out, and reshape_supersedes_markdown gives providers a way to tell both dispatch paths to prefer the reshaped data over the backend's markdownFormatted string.

  • reshape_list_threads / reshape_thread rewrites each thread into {id, subject, from, date, date_local, labels, snippet, messageCount}, picking the newest message by internalDate for header data; the case-sensitive dispatch bug (previous thread) is fixed by switching to eq_ignore_ascii_case.
  • reshape_supersedes_markdown defaults to false (no change for any existing action), returns true only for GMAIL_LIST_THREADS without raw_html, and correctly keeps GMAIL_FETCH_EMAILS false so its reshape can still read markdownFormatted for the message body.
  • Both ComposioExecuteTool and ComposioActionTool gain external_effect_with_args, routing write/admin Composio actions through the approval gate; absent or unrecognised slugs default to gated (safe direction).

Confidence Score: 5/5

Safe to merge — the change is scoped to one action (GMAIL_LIST_THREADS), the default for all other actions and toolkits is byte-identical to before, and the addressed bugs are covered by regression tests.

The reshaping logic is narrow and well-bounded. reshape_supersedes_markdown defaults to false, so no other action or toolkit is affected. Both dispatch paths apply the same reshape with the same raw_html guard. The previous case-sensitivity and raw_html issues were fixed before this diff was finalised and are regression-tested. The external_effect_with_args gate uses the same scope-classification already in use by the async dispatcher, and errs toward gating on unknown input.

Files Needing Attention: No files require special attention.

Important Files Changed

Filename Overview
src/openhuman/memory/sync/composio/providers/gmail/post_process.rs Adds reshape_list_threads / reshape_thread, fixes case-sensitive dispatch to use eq_ignore_ascii_case, and promotes is_raw_html_flag_set to pub(super) for use by provider.rs.
src/openhuman/memory/sync/composio/providers/gmail/provider.rs Implements reshape_supersedes_markdown returning true only for GMAIL_LIST_THREADS without raw_html; well-tested with supersede, non-supersede, and raw_html edge cases.
src/openhuman/memory/sync/composio/providers/traits.rs Adds reshape_supersedes_markdown to the ComposioProvider trait with a safe false default; doc block clearly documents the per-action vs per-toolkit design choice and the raw_html edge case.
src/openhuman/integrations/composio/tools.rs Extracts resolve_action_scope_sync from the async wrapper, adds action_mutates_external_state, and implements external_effect_with_args on ComposioExecuteTool — correctly errs toward gating on absent/empty slugs via is_none_or.
src/openhuman/integrations/composio/action_tool.rs Adds external_effect_with_args and the reshape+supersede block to ComposioActionTool, mirroring ComposioExecuteTool; reshape_args clone is necessary and correctly timed before the dispatch consumes args.
src/openhuman/integrations/composio/tools_tests.rs Adds execute_tool_gates_writes_but_not_reads_via_external_effect covering send, delete, read, and absent-slug cases for the dispatcher's approval gate.

Sequence Diagram

sequenceDiagram
    participant Agent
    participant Gate as ApprovalGate
    participant Tool as ComposioExecuteTool / ComposioActionTool
    participant Provider as GmailProvider
    participant Backend as Composio Backend

    Agent->>Tool: call(args)
    Tool->>Tool: external_effect_with_args(args)
    alt slug is write/admin or absent
        Tool->>Gate: route through approval
        Gate-->>Agent: show approval card
        Agent-->>Gate: approve
    end
    Tool->>Backend: execute action
    Backend-->>Tool: "resp { data, markdown_formatted }"
    Tool->>Tool: "reshape_args = args.clone()"
    Tool->>Provider: "post_process_action_result(slug, reshape_args, &mut resp.data)"
    Note over Provider: reshape_list_threads() rewrites data in-place
    Tool->>Provider: reshape_supersedes_markdown(slug, reshape_args)
    alt GMAIL_LIST_THREADS and not raw_html
        Provider-->>Tool: true
        Tool->>Tool: "resp.markdown_formatted = None"
        Tool-->>Agent: reshaped JSON (slim thread summaries)
    else any other action or raw_html
        Provider-->>Tool: false
        Tool-->>Agent: resp.markdown_formatted (backend rendering)
    end
Loading

Reviews (3): Last reviewed commit: "fix(composio): let the reshape and the s..." | Re-trigger Greptile

Comment thread src/openhuman/memory_sync/composio/providers/gmail/post_process.rs Outdated
Comment thread src/openhuman/memory/sync/composio/providers/gmail/post_process.rs

@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

🧹 Nitpick comments (1)
src/openhuman/composio/tools.rs (1)

1530-1560: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Gate provider post-processing on resp.successful in both dispatch paths. Both ComposioExecuteTool::execute and ComposioActionTool::execute invoke provider.post_process_action_result and provider.reshape_supersedes_markdown unconditionally on Ok(resp), without checking resp.successful. The trait doc in traits.rs (lines 214-227) states the hook is only meant to fire for successful responses: "Errors from upstream are not routed here; only successful responses." The shared root cause is one missing resp.successful guard before the provider hook runs in each dispatch path.

  • src/openhuman/composio/tools.rs#L1530-L1560: wrap the provider.post_process_action_result(...) / reshape_supersedes_markdown(...) block in if resp.successful { ... }.
  • src/openhuman/composio/action_tool.rs#L318-L348: apply the same resp.successful guard around the equivalent block.
🤖 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/openhuman/composio/tools.rs` around lines 1530 - 1560, Gate the provider
post-processing blocks on resp.successful so hooks only run for successful
responses. In src/openhuman/composio/tools.rs lines 1530-1560, wrap the
post_process_action_result and reshape_supersedes_markdown logic in the
ComposioExecuteTool::execute path; apply the same guard to the equivalent block
in src/openhuman/composio/action_tool.rs lines 318-348 for
ComposioActionTool::execute.
🤖 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/openhuman/memory_sync/composio/providers/gmail/post_process.rs`:
- Around line 68-73: Update the slug dispatch in the Gmail post-processing
function to use case-insensitive matching for both GMAIL_FETCH_EMAILS and
GMAIL_LIST_THREADS, while preserving the existing fallback behavior for unknown
slugs. Ensure lowercase or mixed-case tool arguments invoke the corresponding
reshape function.

---

Nitpick comments:
In `@src/openhuman/composio/tools.rs`:
- Around line 1530-1560: Gate the provider post-processing blocks on
resp.successful so hooks only run for successful responses. In
src/openhuman/composio/tools.rs lines 1530-1560, wrap the
post_process_action_result and reshape_supersedes_markdown logic in the
ComposioExecuteTool::execute path; apply the same guard to the equivalent block
in src/openhuman/composio/action_tool.rs lines 318-348 for
ComposioActionTool::execute.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c11e9440-0745-4768-a072-4bd31fea7a2f

📥 Commits

Reviewing files that changed from the base of the PR and between a52a599 and db3f02a.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • src/openhuman/composio/action_tool.rs
  • src/openhuman/composio/tools.rs
  • src/openhuman/composio/tools_tests.rs
  • src/openhuman/memory_sync/composio/providers/gmail/post_process.rs
  • src/openhuman/memory_sync/composio/providers/gmail/provider.rs
  • src/openhuman/memory_sync/composio/providers/traits.rs

Comment thread src/openhuman/memory_sync/composio/providers/gmail/post_process.rs Outdated
yh928 added a commit to yh928/openhuman that referenced this pull request Aug 5, 2026
…e same call

Two ways the pair could disagree, and both handed the model the raw MIME tree —
neither the backend summary nor the slim envelope.

**Casing.** `post_process` dispatched on a case-sensitive match while
`reshape_supersedes_markdown` folded case. `composio_execute` takes the action
as an argument, so a lowercase `gmail_list_threads` reaches both verbatim: the
dispatch fell to its no-op arm, the predicate still said "my version
supersedes", and the rendering was cleared on behalf of a reshape that never
ran. The action is the same action whatever the model capitalises; only one of
the two may decide that, so the dispatch now folds case too.

**`raw_html`.** The flag makes `post_process` return early and leave `data`
untouched — that is its whole point, for `GMAIL_FETCH_EMAILS` where the caller
wants the original body. A slug-only predicate could not see it, so the same
clearing happened for a pass-through response. The answer is a property of the
call rather than of the slug, so `reshape_supersedes_markdown` now takes the
caller's arguments and answers `false` when the reshape was opted out of.

Reported by greptile on tinyhumansai#5323 (P1 + P2).
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 5, 2026
yh928 and others added 3 commits August 5, 2026 10:51
…ults

Two gaps on the agent's Composio execution surface, both diagnosed live.

**Approval (P1).** The human-in-the-loop approval card is raised only for tools
whose `external_effect_with_args` is true, but neither `composio_execute` nor
the per-action `ComposioActionTool` declared it — so a Composio mail send
(`GMAIL_SEND_EMAIL`) fired with no approval prompt at all, even when the user
had "ask before sending" configured. The contract gate (schema-presence) and
`permission_level = Write` (channel caps) do not raise that card. Both surfaces
now report external-effect for a write/admin-scoped action and stay false for a
pure read, so a write routes through the `ApprovalGate` while a fetch/list flows
through unprompted. Scope is classified synchronously (`resolve_action_scope`'s
body has no `await`, so it is reused via `resolve_action_scope_sync`).

**Reshape (P4, tinyhumansai#2585).** When the agent calls a Composio action directly, a
verbose provider envelope — Gmail's full MIME tree under `payload.parts[]` —
landed in context on the raw-JSON fallback body. The provider response reshape
that slims it (the same one the sync path runs) was only wired into sync; it now
runs inline on the agent execute + per-action paths, so `resp.data` is slimmed
before it can become the tool body. A backend-rendered `markdown_formatted` body
is already clean and unaffected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRSNnqQsokuGmkbpLoLCGy
…patcher does

`ComposioExecuteTool` reshapes then publishes; the per-action tool published
then reshaped. The payload names no reshaped field today, so the order is not
observable — but two surfaces describing the same action must not disagree
about which snapshot the event saw, or the first field added to it diverges
silently between them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRSNnqQsokuGmkbpLoLCGy
…rendering

GMAIL_LIST_THREADS is rendered by the backend as a bare list of thread ids.
The payload behind it carries each thread's subject, sender, date, labels, and
snippet, and reshape_list_threads lifts them — but both dispatch paths build
the model-facing body by preferring markdownFormatted and falling back to the
JSON envelope only when absent, so the reshape was written and then never read.

Live, a sub-agent searching for mail that does exist got the ids, had nothing
to recognise the thread by, and reported it could not be found.

ComposioProvider::reshape_supersedes_markdown(slug) lets a provider say its
rewrite replaces the rendering; both call sites clear markdown_formatted when
it answers true. Per-action, not per-toolkit, because within Gmail the answer
differs: GMAIL_FETCH_EMAILS's reshape READS markdownFormatted for the message
body, so clearing it there would throw the body away.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRSNnqQsokuGmkbpLoLCGy
yh928 added a commit to yh928/openhuman that referenced this pull request Aug 5, 2026
…e same call

Two ways the pair could disagree, and both handed the model the raw MIME tree —
neither the backend summary nor the slim envelope.

**Casing.** `post_process` dispatched on a case-sensitive match while
`reshape_supersedes_markdown` folded case. `composio_execute` takes the action
as an argument, so a lowercase `gmail_list_threads` reaches both verbatim: the
dispatch fell to its no-op arm, the predicate still said "my version
supersedes", and the rendering was cleared on behalf of a reshape that never
ran. The action is the same action whatever the model capitalises; only one of
the two may decide that, so the dispatch now folds case too.

**`raw_html`.** The flag makes `post_process` return early and leave `data`
untouched — that is its whole point, for `GMAIL_FETCH_EMAILS` where the caller
wants the original body. A slug-only predicate could not see it, so the same
clearing happened for a pass-through response. The answer is a property of the
call rather than of the slug, so `reshape_supersedes_markdown` now takes the
caller's arguments and answers `false` when the reshape was opted out of.

Reported by greptile on tinyhumansai#5323 (P1 + P2).
@yh928
yh928 force-pushed the fix/gmail-thread-list-body branch from 29afd8d to 4b3b202 Compare August 5, 2026 01:54
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/openhuman/integrations/composio/tools.rs`:
- Around line 1543-1559: Run provider post-processing only for successful
responses: in src/openhuman/integrations/composio/tools.rs#L1543-L1559, guard
both ComposioProvider::post_process_action_result and
reshape_supersedes_markdown with resp.successful; apply the same guard in
src/openhuman/integrations/composio/action_tool.rs#L324-L342 before publishing
and serializing the failure response.

In `@vendor/tinyflows`:
- Line 1: Update the vendor/tinyflows submodule gitlink to a commit that exists
in the configured upstream repository, replacing the unreachable
ff9950186ac66775a554c80aa420920b4f39e4de pin. Verify the new pin can be fetched
from https://github.com/tinyhumansai/tinyflows.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4c1037e4-ee99-46c9-8510-d428c807584d

📥 Commits

Reviewing files that changed from the base of the PR and between d75b0a4 and 4b3b202.

📒 Files selected for processing (9)
  • src/openhuman/integrations/composio/action_tool.rs
  • src/openhuman/integrations/composio/tools.rs
  • src/openhuman/integrations/composio/tools_tests.rs
  • src/openhuman/memory/sync/composio/providers/gmail/post_process.rs
  • src/openhuman/memory/sync/composio/providers/gmail/provider.rs
  • src/openhuman/memory/sync/composio/providers/traits.rs
  • vendor/tinychannels
  • vendor/tinyflows
  • vendor/tinyhumans-sdk

Comment thread src/openhuman/integrations/composio/tools.rs Outdated
Comment thread vendor/tinyflows Outdated
…e same call

Two ways the pair could disagree, and both handed the model the raw MIME tree —
neither the backend summary nor the slim envelope.

**Casing.** `post_process` dispatched on a case-sensitive match while
`reshape_supersedes_markdown` folded case. `composio_execute` takes the action
as an argument, so a lowercase `gmail_list_threads` reaches both verbatim: the
dispatch fell to its no-op arm, the predicate still said "my version
supersedes", and the rendering was cleared on behalf of a reshape that never
ran. The action is the same action whatever the model capitalises; only one of
the two may decide that, so the dispatch now folds case too.

**`raw_html`.** The flag makes `post_process` return early and leave `data`
untouched — that is its whole point, for `GMAIL_FETCH_EMAILS` where the caller
wants the original body. A slug-only predicate could not see it, so the same
clearing happened for a pass-through response. The answer is a property of the
call rather than of the slug, so `reshape_supersedes_markdown` now takes the
caller's arguments and answers `false` when the reshape was opted out of.

Reported by greptile on tinyhumansai#5323 (P1 + P2).
@yh928
yh928 force-pushed the fix/gmail-thread-list-body branch from 4b3b202 to cb5dcc3 Compare August 5, 2026 02:10

@greptile-apps greptile-apps Bot 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.

yh928 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

Both execute paths reshaped every `Ok(resp)` before looking at
`resp.successful`. A failure carries the provider's diagnostics in `data`, so a
reshaper written against the success shape rewrote them into an empty or
wrong-shaped record — and `reshape_supersedes_markdown` then cleared the
backend's error rendering on behalf of a reshape that had found nothing. The
model was left with neither the error nor the diagnostics, which is the same
class of loss this PR opened to fix, on the other branch.

The rule is named rather than repeated: `provider_for_reshape(slug, successful)`
is the one place that says a reshape needs both a registered provider and a
response worth reshaping, and both `ComposioExecuteTool` and `ComposioActionTool`
now ask it. Repeating the `resp.successful` check inline would leave the third
call site to remember it, which is how this one was missed.

Tests: a failed `GMAIL_LIST_THREADS` selects no provider while the successful
one still does (asserted, so the test cannot pass vacuously), and an unknown
slug selects none either way. providers 307, composio::tools 104 pass.

Reported by CodeRabbit on tinyhumansai#5323.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRSNnqQsokuGmkbpLoLCGy

@greptile-apps greptile-apps Bot 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.

yh928 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/openhuman/integrations/composio/action_tool.rs`:
- Around line 152-160: Add stable debug logging across the new Composio domain
paths: in action_tool.rs lines 152-160, log the resolved scope and approval
decision using a [domain] or [rpc] prefix and correlation field; in
action_tool.rs lines 318-350, log reshape entry, provider selection,
supersession decisions, and exit with correlation data without logging
reshape_args or response data; in registry.rs lines 71-76, log failed-response
rejection and successful provider resolution with safe slug metadata.

In `@src/openhuman/memory/sync/composio/providers/registry.rs`:
- Around line 181-188: Update
a_failed_response_selects_no_provider_to_reshape_with so it does not register
the shared "gmail" slug with DummyProvider; register a unique test toolkit
instead and use a corresponding action slug in both provider_for_reshape calls,
preserving the success assertion and failed-response assertion.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bad4328e-a1f3-41b8-a848-197bb67f97d8

📥 Commits

Reviewing files that changed from the base of the PR and between d75b0a4 and 0f5b7da.

📒 Files selected for processing (8)
  • src/openhuman/integrations/composio/action_tool.rs
  • src/openhuman/integrations/composio/tools.rs
  • src/openhuman/integrations/composio/tools_tests.rs
  • src/openhuman/memory/sync/composio/providers/gmail/post_process.rs
  • src/openhuman/memory/sync/composio/providers/gmail/provider.rs
  • src/openhuman/memory/sync/composio/providers/mod.rs
  • src/openhuman/memory/sync/composio/providers/registry.rs
  • src/openhuman/memory/sync/composio/providers/traits.rs
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/openhuman/integrations/composio/tools_tests.rs
  • src/openhuman/memory/sync/composio/providers/traits.rs
  • src/openhuman/memory/sync/composio/providers/gmail/provider.rs
  • src/openhuman/memory/sync/composio/providers/gmail/post_process.rs
  • src/openhuman/integrations/composio/tools.rs

Comment thread src/openhuman/integrations/composio/action_tool.rs
Comment thread src/openhuman/memory/sync/composio/providers/registry.rs Outdated
…tion

The provider registry is process-global, so registering a `DummyProvider` under
the `gmail` slug hands it to any parallel test that looks Gmail up — the RwLock
stops the data race, not the semantic one. Uses a `reshapeprobe` toolkit of the
test's own instead; `toolkit_from_slug` splits on the first `_`, so
`RESHAPEPROBE_LIST_THINGS` resolves to it and nothing real is displaced.

providers::registry 6 pass.

Reported by CodeRabbit on tinyhumansai#5323.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRSNnqQsokuGmkbpLoLCGy

@greptile-apps greptile-apps Bot 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.

yh928 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

Both new domain branches decided something and said nothing. Adds a debug event
for the per-action approval classification (`external_effect`, i.e. whether this
slug routes through the gate) and one for reshape-provider selection
(`successful`, the condition that was silently wrong before), and puts the
existing supersede log on the `composio` target so all three carry the tool slug
as their correlation field.

integrations::composio tests pass.

Reported by CodeRabbit on tinyhumansai#5323.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRSNnqQsokuGmkbpLoLCGy

@greptile-apps greptile-apps Bot 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.

yh928 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

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

Labels

bug rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure.

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

GMAIL_LIST_THREADS reshape never reaches the model — the backend rendering wins

1 participant