Skip to content

feat(orchestration): surface tool_result outcome + per-session message_count#4712

Merged
senamakel merged 8 commits into
tinyhumansai:mainfrom
sanil-23:feat/orchestration-tool-result-outcome
Jul 9, 2026
Merged

feat(orchestration): surface tool_result outcome + per-session message_count#4712
senamakel merged 8 commits into
tinyhumansai:mainfrom
sanil-23:feat/orchestration-tool-result-outcome

Conversation

@sanil-23

@sanil-23 sanil-23 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • OrchestrationMessage now carries ok / is_error / exit_code, decoded from the v2 harness tool_result payload, so a failed tool run is distinguishable from a successful one instead of both collapsing to plain body text.
  • New fields are persisted via additive, migration-guarded messages columns (add_column_if_missing), matching the existing event_kind/tool_name/call_id pattern — existing rows read back as NULLNone.
  • SessionSummary gains message_count (total persisted messages per session), populated via the existing store::count_messages.
  • No other surface changes; this is plumbing that unblocks the orchestration UI work.

Problem

The orchestration message stream flattened the v2 ToolResultPayload to body only, dropping ok/is_error/exit_code. Any renderer therefore could not tell a failed tool result from a successful one — they render identically. The roster also had no per-session total message count (only unread).

Solution

  • ingest.rs: capture ok/is_error/exit_code in the ToolResult branch, thread them through ClassifiedMessage and the persisted OrchestrationMessage.
  • store.rs: add the three nullable INTEGER columns to the schema DDL + migrate() (idempotent existence-checked ALTERs), and to insert_message / map_message_row / the three message SELECT lists.
  • schemas.rs: add message_count to the SessionSummary DTO, computed per session in handle_sessions_list via store::count_messages.
  • types.rs: add the fields (camelCase serde, skip-if-none).

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy — ingest classify (success + failing tool_result + non-result rows), store persist/read-back roundtrip (failed + NULL), summarize message_count.
  • Diff coverage ≥ 80% — changed lines exercised by the added Rust unit tests (orchestration::{ingest,store,schemas}).
  • N/A — Coverage matrix: wire/plumbing change, no new user-facing feature row in docs/TEST-COVERAGE-MATRIX.md.
  • N/A — no feature IDs affected (plumbing only).
  • No new external network dependencies introduced.
  • N/A — does not touch release-cut smoke surfaces.
  • N/A — no linked issue (proactive enablement for the orchestration UI work).

Impact

  • Desktop core (Rust) only. Migration: additive nullable columns via add_column_if_missing; a fresh DB gets them from the DDL, an existing store gains them with rows defaulting NULL. No backfill, no breaking change. Serialized fields are skip_serializing_if = Option::is_none, so existing clients are unaffected.

Related

  • Closes:
  • Follow-up PR(s)/TODOs: Frontend PR consuming ok/isError/exitCode + messageCount (orchestration Agent chat + Connections redesign).

AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: feat/orchestration-tool-result-outcome
  • Commit SHA: c1b0c15

Validation Run

  • N/A pnpm --filter openhuman-app format:check — Rust-only change.
  • N/A pnpm typecheck — Rust-only change.
  • Focused tests: cargo test --lib orchestration::{ingest,store,schemas} (46 passed); full orchestration:: suite 387 passed.
  • Rust fmt/check: cargo fmt applied; crate compiles.
  • N/A Tauri fmt/check — app/src-tauri not touched.

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: tool_result outcome + per-session count now available on the wire.
  • User-visible effect: none yet (consumed by the follow-up frontend PR).

Parity Contract

  • Legacy behavior preserved: all new fields optional/nullable; v1 + master/subconscious rows keep None.
  • Guard/fallback/dispatch parity checks: derive_status/readers unchanged; NULL columns read as None.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): N/A
  • Canonical PR: N/A
  • Resolution (closed/superseded/updated): N/A

Summary by CodeRabbit

  • New Features

    • Session lists now show the total number of messages in each session.
    • Tool result messages can now display success/failure details, including error status and exit code when available.
  • Bug Fixes

    • Tool result outcome details are now saved and returned consistently across message history and session views.
    • Existing records continue to load correctly, with new fields left blank when not applicable.

…e_count

OrchestrationMessage now carries ok/is_error/exit_code decoded from the v2
tool_result payload, persisted via additive, migration-guarded messages columns.
A failed tool run is therefore distinguishable from a successful one on read
instead of both collapsing to plain output.

SessionSummary gains message_count (total persisted messages per session) via
the existing store::count_messages, for the roster's per-session count.

Both unblock the orchestration UI work (failed-result styling + session counts)
without touching any other surface.
@sanil-23 sanil-23 requested a review from a team July 8, 2026 15:44
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 53 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0bf254d8-b67a-40f5-a631-4a4bddf65933

📥 Commits

Reviewing files that changed from the base of the PR and between 33743fa and cf122dc.

📒 Files selected for processing (5)
  • app/src-tauri/src/telegram_scanner/mod.rs
  • src/openhuman/orchestration/ingest.rs
  • src/openhuman/orchestration/schemas.rs
  • src/openhuman/orchestration/store.rs
  • src/openhuman/orchestration/types.rs
📝 Walkthrough

Walkthrough

Adds optional ok, is_error, and exit_code outcome fields for tool_result messages across ingest classification, storage types, SQLite schema/migration, and message read/write paths. Also adds a message_count field to session summary DTOs, computed in the sessions list handler and defaulted to zero elsewhere.

Changes

Tool result outcome and session message count

Layer / File(s) Summary
Outcome field type definitions
src/openhuman/orchestration/types.rs, src/openhuman/orchestration/ingest.rs
OrchestrationMessage, ClassifiedMessage, and V2Body gain optional ok, is_error, exit_code fields with default None.
Classification and persistence wiring
src/openhuman/orchestration/ingest.rs
classify_v2 populates outcome fields from decrypted tool_result payloads; other classification paths set them to None; persist_message threads values through; tests updated.
SQLite schema, migration, and message read/write
src/openhuman/orchestration/store.rs
Adds ok/is_error/exit_code columns to messages DDL, adds additive migration, updates insert_message and all message SELECT/mapping paths, and adds/expands round-trip and migration tests.
Session message_count in roster payload
src/openhuman/orchestration/schemas.rs
SessionSummary and summarize(...) gain message_count; sessions_list computes it via store counting, sessions_create and pinned placeholder default it to 0; tests updated.

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

Possibly related PRs

Suggested labels: feature, rust-core

Poem

A rabbit checks each tool_result's fate,
ok or error, exit code and state,
Columns added, migrations run clean,
Sessions now count each message seen,
Hop hop hooray for data complete! 🐇✨

🚥 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 clearly matches the main changes: tool_result outcome fields and per-session message_count.
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.

@sanil-23

sanil-23 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 33743fafe2

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/openhuman/orchestration/ingest.rs Outdated
b.body = p.output;
b.call_id = non_empty(p.call_id);
// Carry the outcome so the renderer can distinguish a failed run.
b.ok = Some(p.ok);

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 Preserve unknown tool outcomes instead of failing them

When a v2 tool_result comes from an older or partial harness payload that omits ok, ToolResultPayload accepts it via #[serde(default)] and p.ok becomes false; persisting Some(false) here makes the frontend failure check (ok === false) render that otherwise-successful/unknown result as failed even when is_error is false and exit_code is absent or zero. Only set ok when the wire field was actually present, or derive it from the explicit error/exit fields.

Useful? React with 👍 / 👎.

Comment thread src/openhuman/orchestration/schemas.rs Outdated
Comment on lines +402 to +403
let message_count =
store::count_messages(conn, &session.agent_id, &session.session_id)?;

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 Count only visible transcript messages

In sessions with v2 session_info, status, lifecycle, or unknown events, this uses count_messages, which counts rows that messages_list, unread_count, and latest_message_preview deliberately filter out. The Connections UI can therefore show a nonzero or steadily growing “N messages” count while the transcript is empty or unchanged due to hidden status traffic; use the same visibility predicate as the transcript query for the summary count.

Useful? React with 👍 / 👎.

@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 (2)
src/openhuman/orchestration/schemas.rs (1)

365-424: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Per-session count_messages query adds another round-trip to the sessions-list loop.

Each session now runs unread_count, an optional preview query, and count_messages — an N+1 pattern. orchestration_list_sessions in tools.rs already has the identical shape, so this is consistent with existing precedent rather than a new regression; flagging only as a good-to-have optimization (e.g., a single aggregated GROUP BY session_id count query) if session lists grow large.

🤖 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/orchestration/schemas.rs` around lines 365 - 424,
handle_sessions_list currently does a per-session count_messages lookup inside
the loop, creating an N+1 round-trip pattern alongside unread_count and the
optional preview query. Refactor the sessions_list path to fetch message counts
in one aggregated query up front (for example by session_id) and then reuse that
result when building each SessionSummary, keeping the existing
summarize/current_task logic intact. Use handle_sessions_list as the main entry
point and align the shape with orchestration_list_sessions in tools.rs so the
optimization stays consistent with the existing session-list flow.
src/openhuman/orchestration/store.rs (1)

45-63: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Doc comment doesn't mention the new outcome columns.

The comment above the messages table (lines 45-47) still only documents event_kind/tool_name/call_id; consider extending it to mention ok/is_error/exit_code for consistency with the rest of the file's documentation style.

📝 Suggested doc update
-    // `event_kind`/`tool_name`/`call_id` carry the v2 per-message event shape
-    // (`event.kind` + tool identity/correlation). Nullable and additive; v1 and
-    // pinned master/subconscious rows leave them NULL.
+    // `event_kind`/`tool_name`/`call_id` carry the v2 per-message event shape
+    // (`event.kind` + tool identity/correlation). `ok`/`is_error`/`exit_code`
+    // carry the `tool_result` outcome. Nullable and additive; v1 and pinned
+    // master/subconscious rows leave them NULL.
🤖 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/orchestration/store.rs` around lines 45 - 63, The `messages`
table doc comment in `store.rs` is outdated because it only describes
`event_kind`/`tool_name`/`call_id` and omits the new outcome columns. Update the
comment above `CREATE TABLE IF NOT EXISTS messages` to also document `ok`,
`is_error`, and `exit_code`, keeping the wording consistent with the existing
schema comments in `messages`/`store.rs`. Use the table definition as the source
of truth and make sure the new fields are mentioned alongside the other nullable
additive v2 columns.
🤖 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/orchestration/schemas.rs`:
- Around line 365-424: handle_sessions_list currently does a per-session
count_messages lookup inside the loop, creating an N+1 round-trip pattern
alongside unread_count and the optional preview query. Refactor the
sessions_list path to fetch message counts in one aggregated query up front (for
example by session_id) and then reuse that result when building each
SessionSummary, keeping the existing summarize/current_task logic intact. Use
handle_sessions_list as the main entry point and align the shape with
orchestration_list_sessions in tools.rs so the optimization stays consistent
with the existing session-list flow.

In `@src/openhuman/orchestration/store.rs`:
- Around line 45-63: The `messages` table doc comment in `store.rs` is outdated
because it only describes `event_kind`/`tool_name`/`call_id` and omits the new
outcome columns. Update the comment above `CREATE TABLE IF NOT EXISTS messages`
to also document `ok`, `is_error`, and `exit_code`, keeping the wording
consistent with the existing schema comments in `messages`/`store.rs`. Use the
table definition as the source of truth and make sure the new fields are
mentioned alongside the other nullable additive v2 columns.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 122ac26d-e85e-4b8c-9cc9-0f926b9eaff1

📥 Commits

Reviewing files that changed from the base of the PR and between 003e8c5 and 33743fa.

📒 Files selected for processing (4)
  • src/openhuman/orchestration/ingest.rs
  • src/openhuman/orchestration/schemas.rs
  • src/openhuman/orchestration/store.rs
  • src/openhuman/orchestration/types.rs

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 8, 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: 592341d427

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/openhuman/orchestration/schemas.rs Outdated
Comment on lines +402 to +403
let message_count =
store::count_messages(conn, &session.agent_id, &session.session_id)?;

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 Count pinned master messages with the thread scope

When the existing master window contains rows for more than one agent_id (for example local Master asks plus peer plain DMs/outbound steering), the transcript reader aggregates that pinned chat by session_id only via store::list_messages_by_session, while this new message_count is scoped to the current session row's agent_id. In that context the roster can report a messageCount smaller than the number of messages shown when opening the Master thread; use the same session-only scope for pinned windows or keep their count at the placeholder value.

Useful? React with 👍 / 👎.

@senamakel senamakel merged commit 9175603 into tinyhumansai:main Jul 9, 2026
14 checks passed
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