Skip to content

fix(flows): resolve a non-owner Slack DM recipient via a lookup node#4955

Merged
graycyrus merged 2 commits into
tinyhumansai:mainfrom
graycyrus:fix/flows-nonowner-dm-lookup
Jul 16, 2026
Merged

fix(flows): resolve a non-owner Slack DM recipient via a lookup node#4955
graycyrus merged 2 commits into
tinyhumansai:mainfrom
graycyrus:fix/flows-nonowner-dm-lookup

Conversation

@graycyrus

@graycyrus graycyrus commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

The merged self-DM fix (#4933/#4941) surfaces the connected owner's own
platform_user_id on list_flow_connections, so "DM me" wires correctly.
But when the user asks the workflow_builder to DM someone else by
name or email (a teammate, "DM Alan Johnson"), the builder had no way to
resolve that person's Slack member id — no matching platform_user_id
exists for a non-owner, so it either nagged the user or built a broken
graph.

This is a prompt-only fix — no new Composio action, no new tool code.
SLACK_FIND_USERS and SLACK_LIST_ALL_USERS were already in the curated
Slack catalog (src/openhuman/memory_sync/composio/providers/catalogs_messaging.rs,
lines 12-15 and 28-31), which means they already come back featured: true
from search_tool_catalog and are fully discoverable — nothing to change
there.

Resolution mechanism (confirmed against the real Composio contract)

I pulled the live Slack tool contracts from tests/fixtures/composio_slack.json
(a captured Composio catalog snapshot) to confirm the exact input shape
rather than guessing:

  • SLACK_FIND_USERS accepts an email parameter directly — "This is a
    convenience parameter that automatically performs an email-based search."
    Paired with exact_match: true, this uses "Slack's dedicated email lookup
    endpoint" and returns at most one match, which is what makes it safe to
    bind into the send node without asking the user anything.
  • SLACK_FIND_USERS also accepts search_query for a name-based search,
    but per its own description "Name-based queries can return multiple
    matches — verify exactly one user ID before passing to downstream tools."
    So a name-only ask is only auto-wired when it resolves to exactly one
    match; otherwise the builder must ask the user to confirm (bucket-3
    ambiguity), matching the existing "never DM an unverified same-name
    person" posture.
  • SLACK_LIST_ALL_USERS + a downstream transform/code filter on
    email/display_name/real_name is documented as the fallback only for
    when SLACK_FIND_USERS itself can't resolve the person (e.g. a workspace
    that restricts email visibility) — SLACK_FIND_USERS explicitly
    recommends itself over the list+filter approach ("Prefer SLACK_FIND_USERS
    for targeted lookups").

What changed

  • src/openhuman/flows/agents/workflow_builder/prompt.md — extended the
    existing "to me / message me / DM me" bucket-2 guidance (added by fix(flows): surface connected platform_user_id so the builder wires self-DMs (no #general fallback) #4933)
    with a new case: a named recipient who is not the connected owner.
    Teaches the builder to wire a tool_call lookup node
    (SLACK_FIND_USERS { email, exact_match: true }, or the
    SLACK_LIST_ALL_USERS + filter fallback for a name-only ask) upstream of
    the SLACK_SEND_MESSAGE node, then bind the resolved id into channel
    via an = expression — plus a concise worked example (schedule →
    find-user-by-email → send-DM). The existing owner platform_user_id fast
    path and the "ask when ambiguous" safety rule are both preserved verbatim.
  • src/openhuman/flows/agents/workflow_builder/builder_prompt.rs
    extended standing_prompt_teaches_plain_language_and_readonly_memory
    (the existing contract test for the standing prompt) with new positive
    assertions covering the non-owner case, the SLACK_FIND_USERS
    email/exact_match wiring, the SLACK_LIST_ALL_USERS fallback, and the
    ask-when-ambiguous safety rule — mirroring the style of the existing
    self-DM assertions from fix(flows): surface connected platform_user_id so the builder wires self-DMs (no #general fallback) #4933.

Deviations from the brief

  • No catalog change was needed — both actions were already curated/featured,
    so the "only if the catalog genuinely lacks it" branch didn't apply.
  • No new tool code — this is additive prompt guidance only, per the
    "prompt-first" instruction.

Test plan

  • GGML_NATIVE=OFF cargo check --manifest-path Cargo.toml — clean
  • GGML_NATIVE=OFF cargo test --lib openhuman::flows:: — 387 passed, 0
    failed, including the extended
    builder_prompt::tests::standing_prompt_teaches_plain_language_and_readonly_memory
  • cargo fmt --manifest-path Cargo.toml — clean (no other diffs)

Summary by CodeRabbit

  • New Features
    • Improved direct-message workflow setup when recipients are specified by name, including platform-agnostic recipient lookup via available search tools.
    • Added safety to ensure lookups resolve to exactly one person before sending.
    • Added ambiguity handling: when multiple matches are found, the workflow asks the user to confirm (or use email) rather than sending.
    • Enhanced validation of the send step by checking its contract for any required “open conversation/create DM” prerequisite before sending.

…ia a lookup node

The self-DM fix (tinyhumansai#4933/tinyhumansai#4941) surfaces the connected owner's own
platform_user_id so "DM me" works, but gave the workflow_builder no way to
resolve a NAMED recipient who isn't the account owner (e.g. "DM Alan
Johnson" or an email). Extend the builder's standing prompt so a non-owner
DM target is resolved via an upstream tool_call lookup node
(SLACK_FIND_USERS with email + exact_match for a safe single match, or
SLACK_LIST_ALL_USERS + a filter as fallback), with the resolved id bound
into the send node's channel arg via an = expression. Preserves the
existing owner platform_user_id fast path and the ask-when-ambiguous
safety rule for unverified name matches. No new Composio action needed —
SLACK_FIND_USERS/SLACK_LIST_ALL_USERS were already curated (featured) and
discoverable via search_tool_catalog.
@graycyrus
graycyrus requested a review from a team July 16, 2026 11:00
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The workflow-builder prompt now explains platform-agnostic resolution of non-owner direct-message recipients, ambiguity handling, contract-validated wiring, and optional conversation setup. Regression assertions verify the guidance and reject toolkit-specific hardcoding.

Changes

Non-owner DM resolution

Layer / File(s) Summary
Prompt guidance and regression coverage
src/openhuman/flows/agents/workflow_builder/prompt.md, src/openhuman/flows/agents/workflow_builder/builder_prompt.rs
The prompt documents toolkit-scoped lookup, exact-match preference, ambiguity confirmation, list-and-filter fallback, resolved-ID binding, and optional conversation opening; regression assertions verify these instructions and reject platform-specific identifiers.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Possibly related PRs

Suggested labels: agent, bug

Suggested reviewers: codeghost21

Poem

I’m a rabbit with a lookup trail,
No guessed-up names shall make us fail.
Find the user, confirm the choice,
Wire the ID with careful voice.
Then send the message—hop, hooray!

🚥 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 matches the main change: resolving a non-owner DM recipient through a lookup node.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

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

@coderabbitai coderabbitai Bot added agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. bug labels Jul 16, 2026

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8e641f57ca

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +642 to +645
- Bind the resolved id into the send node's `channel` arg with an `=`
expression off the lookup node (`get_tool_contract` names the exact
output field; confirm with `dry_run_workflow` rather than guessing),
same as the owner path above.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Open Slack DMs before sending

For non-owner Slack DMs, this tells the builder to pass the user id returned by SLACK_FIND_USERS directly into SLACK_SEND_MESSAGE.channel. I checked the captured Composio Slack contracts in tests/fixtures/composio_slack.json: SLACK_SEND_MESSAGE says DMs need the D-channel returned by SLACK_OPEN_DM and that passing a user ID directly causes channel_not_found, while SLACK_OPEN_DM accepts user IDs and returns channel.id. A flow generated for “DM alan@…” will therefore dry-run with an unverifiable binding but fail at runtime; the prompt should insert an SLACK_OPEN_DM node and bind the send node to its returned DM channel.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch, and confirmed against the same fixture (tests/fixtures/composio_slack.json) — thank you. Rather than hardcoding a Slack-specific SLACK_OPEN_DM insertion (which would violate this project's platform-agnostic rule for builder prompt guidance — the non-owner-DM case has to work identically for Slack/Discord/Telegram/etc.), I generalized the fix: the guidance now has the builder get_tool_contract the send action itself and check whether it names a separate open/create-conversation action as a prerequisite before it can accept a resolved user id as the recipient. If so, the builder wires that lookup as its own tool_call node between the user-lookup node and the send node; if the send action accepts a user id directly (as some toolkits do), it skips straight to the send. This covers the exact Slack SLACK_OPEN_DMSLACK_SEND_MESSAGE case you found without special-casing Slack in the prompt text. See the updated "Check the send action's own get_tool_contract for a required 'open conversation' step first" step in prompt.md.

The initial non-owner-DM guidance hardcoded Slack's SLACK_FIND_USERS /
SLACK_LIST_ALL_USERS slugs and args, violating the project's rule that
builder prompt fixes must be architectural/platform-agnostic. Rework the
guidance to teach the general pattern instead: search_tool_catalog the
target toolkit for its own user-lookup action, wire it as a tool_call node
upstream of the send, prefer an unambiguous email/exact lookup over a
name search (which may return multiple people and requires bucket-3
confirmation), and check the send action's own get_tool_contract for a
required open-conversation prerequisite before wiring the send node —
which generally resolves the "Open Slack DMs before sending" P2 without a
Slack special case. Updates builder_prompt.rs's standing-prompt test to
assert the platform-neutral pattern and to reject the previously-hardcoded
Slack slugs/args.

@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.

🧹 Nitpick comments (1)
src/openhuman/flows/agents/workflow_builder/builder_prompt.rs (1)

403-464: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Add regression assertions for the contract-sensitive parts of this flow.

The test covers discovery, upstream placement, cardinality, ambiguity confirmation, and conversation setup, but not the fallback list/filter path, dry_run_workflow, or the required tool-call envelope (=nodes...item.json.data.<id_field>). The prompt currently contains these rules, so they could regress without this test failing.

Proposed assertions
+        assert!(STANDING_PROMPT.contains("Prefer an **email / exact lookup"));
+        assert!(STANDING_PROMPT.contains("fall back to its \"list users\" style action"));
+        assert!(STANDING_PROMPT.contains("dry_run_workflow"));
+        assert!(STANDING_PROMPT.contains("=nodes.find_alan.item.json.data.<id_field>"));
🤖 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/flows/agents/workflow_builder/builder_prompt.rs` around lines
403 - 464, Add regression assertions alongside the existing STANDING_PROMPT
checks for the fallback list/filter resolution path, the required
dry_run_workflow guidance, and the tool-call envelope using
`=nodes...item.json.data.<id_field>`. Keep these assertions focused on the
prompt contract and ensure they fail if any of those rules are removed or
altered.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In `@src/openhuman/flows/agents/workflow_builder/builder_prompt.rs`:
- Around line 403-464: Add regression assertions alongside the existing
STANDING_PROMPT checks for the fallback list/filter resolution path, the
required dry_run_workflow guidance, and the tool-call envelope using
`=nodes...item.json.data.<id_field>`. Keep these assertions focused on the
prompt contract and ensure they fail if any of those rules are removed or
altered.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a6093434-1e7d-4d3a-bedf-e317dbbced9f

📥 Commits

Reviewing files that changed from the base of the PR and between 30b05d8 and fb5549d.

📒 Files selected for processing (2)
  • src/openhuman/flows/agents/workflow_builder/builder_prompt.rs
  • src/openhuman/flows/agents/workflow_builder/prompt.md

@graycyrus
graycyrus merged commit d09e8c1 into tinyhumansai:main Jul 16, 2026
23 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in Team Openhuman Jul 16, 2026
@graycyrus

Copy link
Copy Markdown
Contributor Author

Addressed the nitpick on builder_prompt.rs:403-464 (fallback list/filter path, dry_run_workflow, and the tool_call bind envelope) — added the four suggested regression assertions verbatim in 7668254. All still-passing (387 flows tests, cargo fmt --check clean).

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

Labels

agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. bug

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

1 participant