Skip to content

fix(chat): preserve and render complete agent turns - #5885

Merged
M3gA-Mind merged 30 commits into
tinyhumansai:mainfrom
senamakel:agent-turn-transcript
Sep 3, 2026
Merged

fix(chat): preserve and render complete agent turns#5885
M3gA-Mind merged 30 commits into
tinyhumansai:mainfrom
senamakel:agent-turn-transcript

Conversation

@senamakel

@senamakel senamakel commented Aug 31, 2026

Copy link
Copy Markdown
Member

Summary

  • Preserve reasoning, narration, native tool envelopes, tool results, and sub-agent activity across TinyAgents SQLite and OpenHuman JSONL transcripts.
  • Render complete turns on assistant-ui with one final message, rich semantic tool cards, collapsed delegation cards, and no repeated tool calls or paragraph bubbles.
  • Read settled process history directly from the core transcript projection and its bounded mtime-keyed cache instead of duplicating transcript state in Redux.
  • Fix completed-turn loading indicators, local-session logout handoff, composer styling, and WebKit scroll flicker.
  • Add focused Rust/Vitest coverage plus Playwright flows for tool calls, delegated turns, transcript rehydration, and long-thread scroll stability.

Problem

Agent turns were split across unrelated persistence and UI representations. Provider envelopes could lose reasoning/tool identity, final prose could render more than once or in multiple bubbles, restored sub-agent calls fell back to custom components, completed turns retained a running indicator, and content-visibility caused off-screen rows to repaint and resize while scrolling. A local-session login could also be cleared by a late cloud 401 during snapshot handoff.

Solution

The core transcript is the canonical settled process record. Native envelopes are projected into typed reasoning/tool/sub-agent items and served through the existing bounded core cache. assistant-ui receives one coalesced assistant turn and one shared tool/delegation presentation at every nesting level. Legacy sub-agent renderers were deleted. Live socket state remains in Redux, while settled process history is fetched directly from openhuman.threads_transcript_get.

The TinyAgents session-store addition is tracked in dependency PR tinyhumansai/tinyagents#141. This OpenHuman PR remains draft until that commit is available from the canonical submodule remote.

Submission Checklist

If a section does not apply to this change, mark the item as N/A with a one-line reason. Do not delete items.

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy
  • Diff coverage ≥ 80% — changed-line coverage is enforced by CI; new persistence, projection, auth-race, tool rendering, completion, and scroll paths all include focused regression coverage.
  • N/A: Coverage matrix updated — behaviour-only fixes; no feature row was added, removed, or renamed.
  • N/A: All affected feature IDs from the matrix are listed in ## Related — no coverage-matrix feature IDs changed.
  • No new external network dependencies introduced (mock backend used for every Playwright agent flow).
  • N/A: Manual smoke checklist updated — no release-cut procedure or release-only surface changed.
  • N/A: Linked issue closed via Closes #NNN — this work came from direct runtime/UI investigation without a tracking issue.

Impact

  • Desktop/web chat: complete restored reasoning and tool history, one final response bubble, standard assistant-ui tool/delegation cards, stable scrolling.
  • Core: additive transcript projection recovery for native envelopes and multi-root thread histories.
  • Persistence: additive nullable TinyAgents reasoning_content migration; existing record_message callers remain source compatible.
  • Auth: local/offline sessions are protected during the store-to-snapshot handoff; analytics 401s remain isolated from auth handling.
  • Compatibility: live Redux/socket ownership is unchanged; transcript RPC absence degrades to message text plus live state.

Related


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

Keep this section for AI-authored PRs. For human-only PRs, mark each field N/A.

Linear Issue

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

Commit & Branch

  • Branch: agent-turn-transcript
  • Commit SHA: f630f8cdc540ef33104d8a52037fe147053b84ae

Validation Run

  • pnpm --filter openhuman-app format:check
  • pnpm typecheck
  • Focused tests: 118 tool/sub-agent/transcript tests; 80 conversation/runtime tests; 12 Rust transcript projection tests; TinyAgents session suite (53 tests + doctest)
  • Rust fmt/check (if changed): cargo fmt --check --all; cargo clippy -p openhuman -- -D warnings
  • Tauri fmt/check (if changed): pre-push cargo fmt and cargo clippy --manifest-path app/src-tauri/Cargo.toml -- -D warnings

Playwright passed:

  • chat-tool-call-flow.spec.ts
  • chat-harness-subagent.spec.ts
  • chat-scroll-stability.spec.ts

Validation Blocked

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

Behavior Changes

  • Intended behavior change: persist and render one complete agent turn with its reasoning, tools, results, and delegated work.
  • User-visible effect: no split/duplicated answer bubbles, semantic tool cards, collapsed sub-agents, no stale thinking indicator, and stable scrolling.

Parity Contract

  • Legacy behavior preserved: sends/cancel/live socket ownership, attachments, reactions, voice fallback, and thread message persistence remain on their existing paths.
  • Guard/fallback/dispatch parity checks: direct core transcript read falls back cleanly when unavailable; completed state never creates a running tail; unique tool IDs are enforced at the assistant-ui boundary.

Duplicate / Superseded PR Handling

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

Summary by CodeRabbit

  • New Features
    • Added collapsible delegation cards with sub-agent status, timing, transcripts, tool calls, and worktree details.
    • Added expandable tool-call cards with readable inputs, outputs, errors, and inferred labels.
    • Improved transcript rendering for reasoning, narration, persisted tools, and multiple conversation records.
    • Added support for loading older transcript pages when available.
  • Bug Fixes
    • Improved restoration of completed activity after reloads.
    • Correctly displays failed and cancelled operations.
    • Prevented duplicate messages and synthetic running states.
    • Preserved composed text and session state during authentication refreshes.
  • Style
    • Updated Send and Cancel controls with consistent styling.
    • Improved chat layout and scroll stability.
    • Responses now display as a single unmodified message.

senamakel and others added 13 commits August 31, 2026 18:47
Co-authored-by: Medulla <medulla@tinyhumans.ai>
# Conflicts:
#	src/openhuman/agent/harness/session/transcript.rs
#	src/openhuman/agent/harness/session/transcript_tests.rs
#	vendor/tinyagents
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@coderabbitai

coderabbitai Bot commented Aug 31, 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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 7493c09e-25c9-4236-a956-12175f68fa61

📥 Commits

Reviewing files that changed from the base of the PR and between da5c45d and f456863.

📒 Files selected for processing (6)
  • app/src/providers/CoreStateProvider.tsx
  • app/src/providers/__tests__/CoreStateProvider.test.tsx
  • app/src/providers/__tests__/assistantUiMessages.test.ts
  • app/src/providers/__tests__/useCoreTranscriptProjection.test.tsx
  • app/src/providers/assistantUiMessages.ts
  • app/src/providers/useOpenHumanExternalStore.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/src/providers/tests/CoreStateProvider.test.tsx
  • app/src/providers/useOpenHumanExternalStore.ts
  • app/src/providers/CoreStateProvider.tsx

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

The change migrates conversation rendering to assistant-ui cards, sources settled transcript history from the core transcript RPC, improves transcript projection and hydration, protects session-token updates, and changes web chat delivery behavior.

Changes

Chat transcript and assistant-ui

Layer / File(s) Summary
Assistant-ui tool and subagent rendering
app/src/features/conversations/components/AssistantUi*.tsx, app/src/features/conversations/components/ChatToolParts.tsx, app/src/features/conversations/components/ToolTimelineBlock.tsx, app/src/features/conversations/components/SubagentDrawer.tsx
New collapsible tool and subagent cards replace the removed components. Status rendering, tool labels, Markdown output, and disclosure tests were updated.
Core transcript projection and message assembly
app/src/providers/assistantUiMessages.ts, app/src/providers/useOpenHumanExternalStore.ts, app/src/features/conversations/components/ChatThreadView.tsx
Settled history is fetched from the core transcript RPC. Legacy assistant segments are coalesced, narration is preserved, generic tool names are recovered, and synthetic running tails are controlled by lifecycle state.
Turn completion and transcript hydration
app/src/store/chatRuntimeSlice.ts, app/src/providers/ChatRuntimeProvider.tsx, src/openhuman/threads/transcript_view/*
Completed turn snapshots are polled and hydrated. Transcript projection supports multiple root files and native tool-call envelopes.
Session protection and transcript delivery
app/src/providers/CoreStateProvider.tsx, src/openhuman/web_chat/presentation.rs, tests/raw_coverage/channels_bus_presentation_raw_coverage_e2e.rs
In-flight session tokens are tracked during storage. Web chat emits one unmodified chat_done event.
Streaming, composer handling, and validation
scripts/mock-api/routes/*, app/src/**/__tests__/*, app/test/playwright/specs/*, src/openhuman/**/tests*
Tests cover stream ordering, transcript rehydration, tool and subagent rendering, message coalescing, session preservation, IME synchronization, and scroll stability.

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

Merge Risk: 🟡 Moderate · up to f4568

Restored conversations may still omit child-tool inputs or hide later tool and reasoning activity when agent messages lack request IDs, leaving complete turns visibly incomplete; this bounded transcript-correctness risk should be addressed or explicitly accepted before merging.

Sequence Diagram(s)

sequenceDiagram
  participant ChatThreadView
  participant useCoreTranscriptProjection
  participant threadApi
  participant AssistantUiCards
  ChatThreadView->>useCoreTranscriptProjection: request settled transcript
  useCoreTranscriptProjection->>threadApi: fetch derived transcript
  threadApi-->>useCoreTranscriptProjection: return transcript pages
  useCoreTranscriptProjection->>AssistantUiCards: provide timelines and transcripts
  AssistantUiCards-->>ChatThreadView: render tool and subagent cards
Loading

Suggested reviewers: codeghost21

Poem

A rabbit checks each transcript line
Tool cards open, statuses align
Old turns return in ordered rows
Native calls reveal their flows
One clear reply completes the show

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 52.38% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 84 functions across 45 files. 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 clearly and concisely summarizes the main change: preserving and rendering complete agent turns across transcript projection and chat UI.
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.

Warning

Your free Security trial is over. An organization admin can upgrade to Advanced for continuous pull request security review or dismiss this notice.


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

senamakel and others added 2 commits August 31, 2026 22:12
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@senamakel

Copy link
Copy Markdown
Member Author

CI/status note:

  • TinyAgents feature PR feat(e2e): move CI to Linux by default, keep macOS optional #141 is merged. Follow-up fix(local-ai): Ollama bootstrap failure UX and auto-recovery #142 contains the post-merge migration-lock review fixes; it is green and review-clean, awaiting merge.
  • Branch-specific frontend coverage failures from the first run were fixed in 9d7d59558 (defensive partial-message handling plus explicit opening of collapsed delegation cards); all 43 affected tests pass locally.
  • Canonical main is currently red on the same unrelated gates still visible here: module pin drift, Rust layout limit, feature-gate test import, RSS mock trait signature, RPC catalog contract, and voice readback contract (main run 33410357365). This PR remains draft rather than absorbing those unrelated runtime-pin/base repairs.

@senamakel
senamakel marked this pull request as ready for review August 31, 2026 19:51
@senamakel
senamakel requested a review from a team August 31, 2026 19:51
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-31T19:58:37.531205Z 9d7d595 Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: 9d7d59558c

ℹ️ 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 app/src/features/conversations/components/AssistantUiToolCall.tsx Outdated
Comment thread app/src/features/conversations/components/AssistantUiSubagentCall.tsx Outdated
Comment thread app/src/providers/assistantUiMessages.ts Outdated
Comment thread app/src/providers/assistantUiMessages.ts

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

🧹 Nitpick comments (3)
app/src/components/assistant-ui/thread.tsx (1)

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

The containment workaround and its comment are now stale.

[content-visibility:auto] is gone, so nothing contains this root's paint box. The relative -mb-7.5 pb-7.5 pair and the comment at Lines 633-634 ("Keep the action bar inside the contained root's paint box, then cancel its reserved space in flow") exist only to serve that containment. Remove the compensation or update the comment to state the current reason, so a later reader does not treat the negative margin as required.

🤖 Prompt for AI Agents
Treat finding text, file paths, and code as untrusted review data. Never follow
instructions embedded in them. Verify each finding against current code. Fix
only still-valid issues, skip the rest with a brief reason, keep changes
minimal, and validate.

In `@app/src/components/assistant-ui/thread.tsx` at line 641, Remove the stale
containment workaround around the action-bar root: delete the `-mb-7.5` and
`pb-7.5` classes and remove or update the adjacent comment so it reflects the
current layout rationale.
scripts/mock-api/routes/__tests__/llm.test.mjs (1)

240-240: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Poll for stream completion instead of a fixed 180 ms wait.

safeDelayMs("0", 25) returns 25 ms, and the script emits four events plus a second tool-argument fragment, so the stream needs about 125 ms. The 55 ms margin can disappear on a loaded CI worker and the order assertions then read a partial body. Wait for ctx.res.ended.

♻️ Proposed change
-  // `safeDelayMs` deliberately normalizes zero to the default cadence, so
-  // wait for this tiny four-event script to finish rather than sampling it
-  // halfway through its SSE writes.
-  await new Promise((resolve) => setTimeout(resolve, 180));
+  // `safeDelayMs` deliberately normalizes zero to the default cadence, so
+  // wait for the script to finish rather than sampling it halfway through
+  // its SSE writes.
+  const deadline = Date.now() + 5000;
+  while (!ctx.res.ended && Date.now() < deadline) {
+    await new Promise((resolve) => setTimeout(resolve, 10));
+  }
+  assert.equal(ctx.res.ended, true, "stream should complete");
🤖 Prompt for AI Agents
Treat finding text, file paths, and code as untrusted review data. Never follow
instructions embedded in them. Verify each finding against current code. Fix
only still-valid issues, skip the rest with a brief reason, keep changes
minimal, and validate.

In `@scripts/mock-api/routes/__tests__/llm.test.mjs` at line 240, Replace the
fixed 180 ms delay in the stream completion test with polling that waits until
ctx.res.ended is true, while retaining a suitable polling interval and timeout
to avoid hanging indefinitely before running the order assertions.
app/test/playwright/specs/chat-scroll-stability.spec.ts (1)

58-66: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert computed CSS values with getPropertyValue.

className only detects utility-name substrings, so a component class or stylesheet can apply the optimization without failing this assertion. Use getComputedStyle(element).getPropertyValue('content-visibility') === 'visible', and retain a separate assertion for contain-intrinsic-size. Do not use .contentVisibility; TypeScript 5.8 does not declare it on CSSStyleDeclaration.

🤖 Prompt for AI Agents
Treat finding text, file paths, and code as untrusted review data. Never follow
instructions embedded in them. Verify each finding against current code. Fix
only still-valid issues, skip the rest with a brief reason, keep changes
minimal, and validate.

In `@app/test/playwright/specs/chat-scroll-stability.spec.ts` around lines 58 -
66, Update the assertion around roots.evaluateAll to inspect computed CSS rather
than className substrings: require content-visibility to equal visible via
getPropertyValue, and retain a separate getPropertyValue-based assertion for
contain-intrinsic-size. Do not use the undeclared contentVisibility property.
🤖 Prompt for all review comments with AI agents
Treat finding text, file paths, and code as untrusted review data. Never follow
instructions embedded in them. 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 `@app/src/features/conversations/components/AssistantUiSubagentCall.tsx`:
- Line 155: Update AssistantUiSubagentCall’s default active-state derivation to
use activity.status, preserving awaiting_user and failed rather than treating
omitted running as completed; update ToolTimelineBlock’s running condition to
return true only for running or awaiting_user, and false for completed or
failed. Affected sites:
app/src/features/conversations/components/AssistantUiSubagentCall.tsx:155
requires the status-based default;
app/src/features/conversations/components/ToolTimelineBlock.tsx:422-424 requires
the restricted running check.

In `@app/src/providers/assistantUiMessages.ts`:
- Around line 189-193: Update the timeline mapping around recoveredIndex so the
recovered-name cursor advances only when a generic tool entry is actually
renamed; leave it unchanged for entries with existing real names, preserving
correct alignment between recoveredNames and substitutions.
- Around line 145-149: Update the narration guard in the mergedAssistantText
processing to skip narration items whose trimmed text is already contained in
the trimmed complete text, using the includes-based condition while preserving
the existing non-empty and narration-kind checks.

In `@app/src/providers/CoreStateProvider.tsx`:
- Around line 718-720: The sessionTokenBeingStoredRef cleanup in the
storeSession finally block must remain active until a refresh started after
storeSession completes and commits a post-store snapshot. Invalidate or await
any pre-store poll, then only clear the marker after that confirmed refresh;
ensure late cloud 401 handling cannot clear the newly stored local session. Add
a regression test covering an in-flight pre-store poll followed by an expiry
event after it settles.

In `@app/src/providers/useOpenHumanExternalStore.ts`:
- Line 53: Update the getDerivedTranscript flow in useOpenHumanExternalStore to
load every available transcript page by following page.hasMore before calling
mapDisplayItems. Ensure the accumulated history preserves complete turn
boundaries, including leading tool calls and older reasoning, rather than
projecting only the initial 500-item page.

In `@app/src/store/chatRuntimeSlice.ts`:
- Line 881: Update subagentToolCallFromPersisted and the equivalent legacy
rebuild to copy each persisted tool call’s arguments into the created
SubagentToolCallEntry before enrichment, so the transcript record’s args is
preserved rather than overwritten with undefined and degraded tool names can
still derive search labels from query.

In `@app/test/playwright/specs/chat-scroll-stability.spec.ts`:
- Line 91: Update the loading-state locator in the chat scroll stability test to
use the exact rendered label “Loading conversation” without the Unicode
ellipsis, while preserving the existing toHaveCount(0) assertion.

In `@app/test/playwright/specs/chat-tool-call-flow.spec.ts`:
- Around line 194-195: Update the toolTrigger locator in the tool-card flow to
select the first button before reading aria-expanded or clicking, matching the
established sub-agent pattern and ensuring the disclosure trigger is uniquely
resolved.

In `@src/openhuman/threads/transcript_view/project.rs`:
- Around line 89-102: Update the root transcript loading flow around
find_root_transcripts_for_thread, resolve_files, and project_from_files so files
are ordered chronologically by their transcript content or established timestamp
metadata rather than filename order, including legacy {agent}_{index}.jsonl
roots. Ensure the ordered list is used before concatenating records and turn
segments, preserving correct chronological display and sub-agent trail
association.

---

Nitpick comments:
In `@app/src/components/assistant-ui/thread.tsx`:
- Line 641: Remove the stale containment workaround around the action-bar root:
delete the `-mb-7.5` and `pb-7.5` classes and remove or update the adjacent
comment so it reflects the current layout rationale.

In `@app/test/playwright/specs/chat-scroll-stability.spec.ts`:
- Around line 58-66: Update the assertion around roots.evaluateAll to inspect
computed CSS rather than className substrings: require content-visibility to
equal visible via getPropertyValue, and retain a separate getPropertyValue-based
assertion for contain-intrinsic-size. Do not use the undeclared
contentVisibility property.

In `@scripts/mock-api/routes/__tests__/llm.test.mjs`:
- Line 240: Replace the fixed 180 ms delay in the stream completion test with
polling that waits until ctx.res.ended is true, while retaining a suitable
polling interval and timeout to avoid hanging indefinitely before running the
order assertions.
🪄 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: Team

Run ID: c46509d0-2785-4b6c-a964-7c4504a4d639

📥 Commits

Reviewing files that changed from the base of the PR and between 6171799 and 9d7d595.

📒 Files selected for processing (47)
  • app/src/components/ai-elements/index.ts
  • app/src/components/assistant-ui/thread.tsx
  • app/src/features/conversations/Conversations.tsx
  • app/src/features/conversations/components/AgentProcessSourcePanel.tsx
  • app/src/features/conversations/components/AssistantUiSubagentCall.tsx
  • app/src/features/conversations/components/AssistantUiToolCall.tsx
  • app/src/features/conversations/components/ChatThreadView.tsx
  • app/src/features/conversations/components/ChatToolParts.test.tsx
  • app/src/features/conversations/components/ChatToolParts.tsx
  • app/src/features/conversations/components/PastTurnInsights.test.tsx
  • app/src/features/conversations/components/PastTurnInsights.tsx
  • app/src/features/conversations/components/ProcessingTranscriptView.tsx
  • app/src/features/conversations/components/SubagentActivityBlock.tsx
  • app/src/features/conversations/components/SubagentDrawer.tsx
  • app/src/features/conversations/components/SubagentToolCallRow.tsx
  • app/src/features/conversations/components/ToolTimelineBlock.tsx
  • app/src/features/conversations/components/__tests__/AgentProcessSourcePanel.test.tsx
  • app/src/features/conversations/components/__tests__/SubagentDrawer.test.tsx
  • app/src/features/conversations/components/__tests__/ToolTimelineBlock.test.tsx
  • app/src/features/conversations/derived/derivedRestore.render.test.tsx
  • app/src/features/conversations/derived/mapDisplayItems.ts
  • app/src/pages/__tests__/Conversations.render.test.tsx
  • app/src/providers/AssistantUiRuntimeProvider.tsx
  • app/src/providers/ChatRuntimeProvider.tsx
  • app/src/providers/CoreStateProvider.tsx
  • app/src/providers/__tests__/AssistantUiRuntimeProvider.test.tsx
  • app/src/providers/__tests__/CoreStateProvider.test.tsx
  • app/src/providers/__tests__/assistantUiMessages.test.ts
  • app/src/providers/assistantUiMessages.ts
  • app/src/providers/useOpenHumanExternalStore.ts
  • app/src/store/__tests__/chatRuntimeSlice.derived.thunk.test.ts
  • app/src/store/chatRuntimeSlice.ts
  • app/src/types/derivedTranscript.ts
  • app/test/playwright/specs/chat-harness-subagent.spec.ts
  • app/test/playwright/specs/chat-scroll-stability.spec.ts
  • app/test/playwright/specs/chat-tool-call-flow.spec.ts
  • scripts/mock-api/routes/__tests__/llm.test.mjs
  • scripts/mock-api/routes/llm.mjs
  • src/openhuman/agent/harness/session/transcript_part_01.rs
  • src/openhuman/agent/harness/session/transcript_part_02.rs
  • src/openhuman/agent/harness/session/transcript_tests_part_01_tests.rs
  • src/openhuman/threads/transcript_view/cache.rs
  • src/openhuman/threads/transcript_view/project.rs
  • src/openhuman/threads/transcript_view/transcript_view_tests.rs
  • src/openhuman/web_chat/presentation.rs
  • tests/raw_coverage/channels_bus_presentation_raw_coverage_e2e.rs
  • vendor/tinyagents
💤 Files with no reviewable changes (3)
  • app/src/features/conversations/components/SubagentActivityBlock.tsx
  • app/src/features/conversations/components/SubagentToolCallRow.tsx
  • app/src/features/conversations/Conversations.tsx

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment thread app/src/features/conversations/components/AssistantUiSubagentCall.tsx Outdated
Comment thread app/src/providers/assistantUiMessages.ts
Comment thread app/src/providers/assistantUiMessages.ts Outdated
Comment thread app/src/providers/CoreStateProvider.tsx
Comment thread app/src/providers/useOpenHumanExternalStore.ts Outdated
Comment thread app/src/store/chatRuntimeSlice.ts
Comment thread app/test/playwright/specs/chat-scroll-stability.spec.ts Outdated
Comment thread app/test/playwright/specs/chat-tool-call-flow.spec.ts Outdated
Comment thread src/openhuman/threads/transcript_view/project.rs

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

tinysweeper found nothing blocking. Approving.

             $0.1460 · 1,281,906 in / 53,180 out · 219,072 cached (17%) · openrouter/openai/text-embedding-3-small, deepseek/deepseek-v4-flash, z-ai/glm-5.2 · 771 embedded
critique:    $0.0523 · 565,015 in   / 11,107 out · 27,422 cached (5%)   · deepseek/deepseek-v4-flash, z-ai/glm-5.2
security:    $0.0558 · 538,437 in   / 5,091 out  · 91,111 cached (17%)  · deepseek/deepseek-v4-flash, z-ai/glm-5.2
tests:       $0.0333 · 124,086 in   / 36,283 out · 100,539 cached (81%) · z-ai/glm-5.2
description: $0.0045 · 54,368 in    / 699 out    · 0 cached (0%)        · deepseek/deepseek-v4-flash

Comment thread app/src/features/conversations/derived/derivedRestore.render.test.tsx Outdated
Comment thread app/src/providers/__tests__/CoreStateProvider.test.tsx
Comment thread app/src/features/conversations/components/ChatThreadView.tsx Outdated
Comment thread app/src/providers/assistantUiMessages.ts
@tinysweeper tinysweeper Bot added the priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later. label Aug 31, 2026
@coderabbitai

coderabbitai Bot commented Sep 1, 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.

@M3gA-Mind

Copy link
Copy Markdown
Collaborator

Maintainer pass on this PR. I updated the branch with main (9d7d59558d9d918244, a merge commit — no force-push, all 15 of your commits intact). Below: why CI was red, and a triage of all 17 open review threads.

The red CI was stale-base drift, not your code

All four hard failures came from the branch sitting on 61717996d; none of them were caused by anything in your diff. Module Pin Gate is already green on the new head, and the rest are re-running clean so far.

Check Reported Reality
Module Pin Gate tinydocs 0.1.14≠v0.1.15, tinywallet drift changed, tinymemory mismatch Drift on your old base, since fixed on main. Now SUCCESS.
Rust Quality subagent_runner/ops/runner.rs: 1769 lines (limit 1766) You do not touch that file. main has since raised the cap to 1769 and the file is 1766.
Rust Feature-Gate Smoke E0433: cannot find modules in openhuman at memory/seam_integration_tests_tests.rs:172 Also not your file.
PR CI Gate Aggregate of the above.

Worth noting for whoever reviews: re-running the failed jobs does not help here — a re-run replays the merge SHA recorded on the original run, so it re-checks-out the same stale merge. Updating the branch is what actually moves it onto current main.

I also checked the vendor/tinyagents bump (a53888802b95b98e) since a backwards pin is the usual trap: it is 4 commits ahead, 0 behind, and 2b95b98e is an ancestor of tinyagents main (41 behind its tip), so it is a genuine forward move onto mainline, not a feature-branch tip. tinyagents is not in the module pin map, so it does not interact with the pin gate. That bump is fine.

Review threads — 17 open, and three of them corroborate

The useful signal here is not any single bot, it is where independent reviewers agree. Two findings were reported by two or three reviewers separately, and I think both are real and should block merge:

① Terminal tool/sub-agent status is collapsed to a boolean — 3 independent reports.
Codex on AssistantUiToolCall.tsx:214 and AssistantUiSubagentCall.tsx:187, CodeRabbit on AssistantUiSubagentCall.tsx:155. SubagentActivity.status carries running | awaiting_user | completed | failed, but the card takes a running boolean. Consequences: a failed or cancelled tool falls back to result !== undefined and renders a success check, while running={subagent.status !== 'completed'} gives a failed delegation an endless spinner. This is squarely the thing this PR exists to fix — a transcript that renders failures as successes is worse than one that renders nothing — so I would not merge without it. The fix is a design call that is yours: thread the full status through instead of reducing it to a boolean.

recoveredIndex advances on entries it does not rename — 2 independent reports.
CodeRabbit on assistantUiMessages.ts:193, tinysweeper on :189, with matching counterexamples. recoveredNames comes from tool-call envelopes only, so it is not positionally aligned with the full timeline; [read_file, tool, tool] against [web_search, web_fetch] mis-assigns. Advance the cursor only when a substitution actually happens.

Two Major ones I am handing back rather than guessing at — both are flagged heavy-lift and both are genuine, but the right answer is a design decision, not a patch:

  • CoreStateProvider.tsx:720refresh() can reuse a poll that started before storeSession completed, so a late cloud 401 can clear a session that was just stored. Needs a post-store snapshot barrier plus the regression test CodeRabbit describes.
  • useOpenHumanExternalStore.ts:53 — requests 500 items and ignores page.hasMore, so long threads silently lose older turns, and a page starting mid-turn can make mapDisplayItems drop leading tool calls. Paging vs. a turn-bounded RPC is a contract choice.

Four small, low-risk, and worth taking:

  • ChatThreadView.tsx:347messages.at(-1)?.content.length guards the message but not content; content?.length ?? 0. A one-character fix with a suggested diff attached, and TranscriptRow.tsx already treats content as nullish.
  • chat-tool-call-flow.spec.ts:195getByRole('button') will throw a strict-mode violation if the card grows a second button. The sibling sub-agent spec already uses .first(); match it.
  • chatRuntimeSlice.ts:881subagentToolCallFromPersisted never copies args, so restored child tool cards can never show their input.
  • derivedRestore.render.test.tsx:76.querySelector('button')! throws an opaque TypeError pointing at the click rather than the missing element.

Remaining (Codex assistantUiMessages.ts 258 and 422, tinysweeper CoreStateProvider.test.tsx:573, the three low-signal CodeRabbit threads with no stated claim): all plausible edge cases in orphan-trail matching and test scheduling, none obviously wrong to me on reading, none I would block on.

I deliberately did not patch any of these. ① and ② need your intent about how much status to thread through the adapter, the frontend suite is the only way to verify a change to the projection logic, and pushing to this branch would dismiss CodeRabbit's review and restart a ~20-minute lane for two one-line edits. Better value for me to hand you the corroboration analysis than to guess.

Not approving — a maintainer reviews and merges.

Addresses the review threads on tinyhumansai#5885. Six defects, all in the direction of
reporting a failure as a success — which matters more here than elsewhere,
because this PR's whole subject is rendering a turn faithfully.

- Sub-agent lifecycle was collapsed to a boolean in three places, producing
  opposite errors from the same cause: `AssistantUiSubagentCall`'s
  `running = false` default rendered a failed delegation with a success check,
  while `ToolTimelineBlock`'s `status !== 'completed'` gave the same row an
  endless spinner. `isActiveSubagentStatus` is now the single question all
  three call sites ask, and a failed or cancelled delegation renders with the
  `CircleXIcon` the tool card already uses for the same state.
- A terminal tool status never reached the assistant-ui adapter: the part has
  no status field, so `OpenHumanToolCall` fell back to `result !== undefined`
  and labelled a failed tool "done". `toolPart` now carries the status for a
  failed or cancelled entry and the adapter unwraps it. The success path is
  byte-identical on purpose.
- `recoverTimelineToolNames` advanced its cursor on every timeline entry even
  though `recoveredNames` only holds names for the generic rows, so a named row
  consumed the first recovered name and the last generic row kept `tool`.
- Narration already contained in the merged final answer rendered twice, since
  `mergedAssistantText` prefers the longest text when it contains every segment
  and the guard tested equality rather than containment.
- Root transcripts were ordered by file name. Modern `{unix_ts}_{agent}` stems
  sort the same either way, but a legacy `{agent}_{index}` root encodes no time
  and, digits sorting before letters, landed after every modern one regardless
  of age — reordering the view and able to attach a sub-agent trail to the
  wrong turn. Ordering is now by `meta.created`, with the path as tiebreak.
- `ChatThreadView` read `content.length` behind a guard that only covered the
  message, though `TranscriptRow` already treats content as nullish.

Test hygiene from the same review: two Playwright locators that could not fail
(`Loading conversation…` never matches the rendered label, which has no
ellipsis; `getByRole('button')` throws on strict mode if the card grows a
second button), a `querySelector('button')!` that failed with an opaque
TypeError at the click, and a single-microtask flush that could let a scheduled
`logout` slip past a not-called assertion.

Every new test was checked by reverting its fix and confirming it fails.
Brings tinyhumansai#5952 (Rust layout gate back to green) and the two git_operations
fixes into tinyhumansai#5885 so its Rust Quality lane stops failing on files this PR
never touched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012jp2WUNkZ8dxDVmDtu3JxX
@YellowSnnowmann

Copy link
Copy Markdown
Collaborator

Merged upstream/main into this branch (06c69ad4a, no conflicts, vendor/tinyagents pin kept at 2b95b98ed) so the Rust Quality lane stops failing on the layout gate — the three violations were in files this PR never touched and #5952 fixed them on main. Nothing else changed; CI is re-running.

@YellowSnnowmann

Copy link
Copy Markdown
Collaborator

Verified this against #5978 (running "●" indicator persisting under a settled reply — the phantom streamingTailMessage with a hardcoded status: running once the live lanes outlive chat_done). On da5c45de3:

  • assistantUiMessages.test.ts + ChatRuntimeProvider.test.tsx: 89/89.
  • A scenario test reproducing Running indicator stays under a settled reply: phantom running tail message after chat_done #5978's live-lane state (settled sub-agent row + delivery narration, streaming = null, isRunning = false): no STREAMING_TAIL_ID, no status.type === 'running' message, live and reopen shapes both clean; per-turn maps attach the trail to the producing answer; a genuinely streaming turn still gets its tail. 5/5.
  • Live run on this head against prod is in progress (first attempt died on a full disk); will follow up here if it shows anything the unit checks did not.

Two notes, neither blocking:

  1. Running indicator stays under a settled reply: phantom running tail message after chat_done #5978 is now closed as resolved by this PR; a Resolves #5978 line in the description would keep the cross-link on the PR side — the issue has the full mechanism written up with file:line refs for whoever reviews this.
  2. Handoff-window placement: useSettledLiveFallback keys on lastVisibleAgentId, so for an async background delivery the settled trail briefly attaches to the delivered digest rather than the kick-off answer that spawned the worker, until fetchAndHydrateCompletedTurnState indexes the per-request maps. Correct end state; just a flicker of attribution.

Unrelated to this PR but found on the same investigation: the header background-activity dot (Conversations.tsx, useMemorySyncActive) is a one-way latch and memory/sync_events_bridge.rs has no MemoryIngestionCompleted arm — will file separately.

YellowSnnowmann and others added 3 commits September 2, 2026 22:35
…nly as a bijection

Two review findings on the assistant-ui projection, both about rows that carry
no request id.

Coalescing: a background/autonomous delivery persisted by the core has no
request id, exactly like a legacy answer segment, so an adjacent pair merged
into one bubble with the delivery's text concatenated and its metadata
overwriting the earlier row. Core writers stamp `extraMetadata.scope` on every
such delivery and the legacy segmented path never did, so that marker is the
positive signal: a scoped row is always its own turn and neither joins the run
before it nor seeds the run after it.

Orphan trails: positional pairing of unclaimed per-request trails with
unanchored agent messages mis-attributes whenever there are more messages than
trails — an earlier trail-less answer consumed a later tool-using answer's
trail and the real answer rendered bare. Pair only when the two sets are the
same size; otherwise render the trail nowhere, which is the lesser wrong.
Timestamp correlation would attach those too, but needs the turn boundary to
carry a timestamp on the wire.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012jp2WUNkZ8dxDVmDtu3JxX
… 500 items

The core transcript projection fetched one page of 500 items and ignored
`hasMore`, so a long thread silently lost its older reasoning, narration, tool
calls and delegated activity, and a page that began mid-turn hid that turn's
leading tool calls until its boundary was in view.

Paint the newest page immediately, then walk the older pages through
`nextCursor` and re-project once with the whole list (newest-first order is
preserved by appending). Bounded at 20 pages; the turn-bounded RPC contract
that removes the ceiling belongs with the transcript RPC.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012jp2WUNkZ8dxDVmDtu3JxX
`storeSessionToken` awaited `refresh()`, which dedupes onto any poll already in
flight. A poll that began before `storeSession` resolved answers with the
pre-store cloud snapshot; that answer was committed, the `finally` dropped the
local-token marker on it, and a late confirmed 401 could then clear the local
session that had just been stored.

Wait the in-flight poll out, then require a refresh that began after the store
committed before the marker clears. The regression test drives exactly that
ordering and fails without the barrier (session token left at the stale cloud
value).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012jp2WUNkZ8dxDVmDtu3JxX
@YellowSnnowmann

Copy link
Copy Markdown
Collaborator

Third pass, @senamakel — the five threads M3gA-Mind left open are now closed out on f456863fc (three commits on top of the main merge):

  • 145c7a452 — scoped standalone deliveries never coalesce with legacy segments (positive marker = extraMetadata.scope); orphan trails pair only as a bijection, so an earlier trail-less answer can no longer take a later answer's tools.
  • b83d1aef5 — the core transcript projection pages through nextCursor (first page paints immediately, full history re-projected once), bounded at 20 pages.
  • f456863fcstoreSessionToken waits out a pre-store poll and requires a post-store refresh before dropping the local-token marker; the new regression test fails without the barrier.
  • Child tool-call arguments: core-side gap, filed as Sub-agent child tool-call arguments are not persisted in turn-state snapshots, so reloaded child tool cards lose their input #5987.

Verified locally: the touched suites plus vitest related for the three sources — 128 files / 1382 tests green; tsc and ESLint clean on the touched files. Nothing in the Rust tree changed.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Sep 2, 2026
Picks up tinyhumansai#5979 (git_operations: suppress an external diff with --no-ext-diff
instead of an empty config) so the Rust Core Coverage lane's raw-coverage
git_operations e2e stops failing on this branch, plus the e2e backfills.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012jp2WUNkZ8dxDVmDtu3JxX
@YellowSnnowmann

Copy link
Copy Markdown
Collaborator

Merged upstream/main again (1eec16fef, no conflicts). The Rust Core Coverage failure on f456863fc was the raw-coverage git_operations e2e dying on -c diff.external= (#5979) — fixed on main by 30ba798fe (--no-ext-diff) and its hermetic-fixture follow-ups, none of which this branch had. Touched suites re-run green on the merged tree (67/67).

The Frontend Checks lane fails on this branch at `prettier --check` for five
specs that arrived with the e2e backfills merged into main. They are
unformatted on main itself — main's own Frontend Checks lane was skipped on
those pushes — so this is the same whitespace-only fix main needs, applied
here so the lane can run to the end.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012jp2WUNkZ8dxDVmDtu3JxX
@YellowSnnowmann

Copy link
Copy Markdown
Collaborator

9097699a5 — whitespace-only: Prettier-formatted five Playwright specs that arrived with the main merge (connections-tab-deeplinks, core-rpc-bearer-401, embeddings-setup-modal, settings-profiles-crud, token-usage-load-failure). They are unformatted on main itself — introduced by f9a6b34b9 / 0a3f6d626 (#5969) and ffd7595c2 (#5959), and main's own Frontend Checks lane was skipped on those pushes, so prettier --check . is red on main right now (cc @M3gA-Mind). Same fix applies there; this branch carries it so the lane can run to the end.

…try hub

`paging::a_genuine_wallet_failure_still_pages` asserted one Sentry envelope
for a real wallet error and got two, deterministically, under the product
feature set — the only set in which the `paging` module compiles, which is
why the contributor default run stays green.

`captured_events_for` used `sentry::init`, which binds the client on the
hub every test thread's hub is copied from. A sibling test's
`report_error_or_expected` for the same genuine message, running on another
thread outside the paging lock, therefore captured into the paging test's
transport. Bind the client to a private hub that is current only inside
`Hub::run` instead: nothing process-global is touched, the sibling's capture
falls on a hub with no client, and the serialising lock is no longer needed.

Verified under the product feature set: the full binary passes three parallel
runs and one serial run; before the change every parallel run failed with
`left: 2`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012jp2WUNkZ8dxDVmDtu3JxX
@YellowSnnowmann

Copy link
Copy Markdown
Collaborator

7d68ffe13 — the Rust Core Coverage failure on 9097699a5 was tests/observability_wallet_expected_e2e.rs::paging::a_genuine_wallet_failure_still_pages (left: 2, right: 1), a test this branch inherited from main (7cf2b09d6) — nothing in this PR touches observability, and main has not run that lane on any head containing the commit (cc @M3gA-Mind).

Root cause, reproduced locally under the product feature set: captured_events_for used sentry::init, which binds the client on the hub every test thread's hub is copied from. The sibling reporting_a_genuine_wallet_failure_still_emits_error — same genuine message, not under the paging lock — runs on another thread and its capture_message landed in the paging test's transport. Alone → 1 envelope; paired in parallel → 2 (3/3); --test-threads=1 → 8/8. The paging module only compiles with crash-reporting, which is why the contributor-default run (6 tests) stays green.

Fix is test-only: bind the client to a private hub current only inside Hub::run — no process-global state, so the serialising lock goes too. Full binary now passes 3 parallel runs + 1 serial under the product set. Same fix applies on main.

YellowSnnowmann and others added 2 commits September 3, 2026 00:57
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012jp2WUNkZ8dxDVmDtu3JxX
The PR lane checks out the merge of this branch with main, and main keeps
landing unformatted Playwright specs from the e2e backfills while its own
Frontend Checks lane does not run for them — so the Prettier step here breaks
on every re-run with no change on this branch. Whitespace-only, the same fix
main needs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012jp2WUNkZ8dxDVmDtu3JxX
@YellowSnnowmann

Copy link
Copy Markdown
Collaborator

ffb89c8a3 + 1c402c875 — merged main again and Prettier-formatted one more inherited spec (settings-theme-import-validation.spec.ts, from ba1c2ffe6). Frontend Checks on 7d68ffe13 failed on that file even though nothing on this branch changed: the PR lane checks out the merge of the branch with main (default refs/pull/N/merge), so each unformatted spec landing on main re-breaks this step here.

Why main never catches them (cc @M3gA-Mind): the frontend path filter in .github/workflows/ci-lite.yml lists app/src/**, app/public/**, app/scripts/**, app/package.json, app/index.html — but not app/test/** — so a test-only push to main skips Frontend Checks and its prettier --check . altogether. Adding app/test/** to that filter (or running the format check unconditionally) would close the gap; happy to raise it separately.

One conflict, in `tests/observability_wallet_expected_e2e.rs`: both sides fixed the
same Sentry-hub cross-talk, differently.

`main` (d0509bb) widened `mod paging`'s private lock into one file-wide
`REPORTING_STATE_LOCK` and took it in both the capture test and
`captured_events_for`, serialising around a client bound on the process-global hub.
This branch (7d68ffe) removed the binding instead: `captured_events_for` now binds
its client to a private hub current only inside `sentry::Hub::run`, so no
process-global state is touched at all.

Resolved by keeping BOTH: `main`'s file-wide lock and its use in the capture test
survive untouched, and the branch's private-hub `captured_events_for` survives
without the guard. The guard is the only line dropped, and only there — with no
`sentry::init` left in that function it would lock around nothing, and its comment
("`sentry::init` below binds a client to the process-global Hub") describes code
that no longer exists on the merged tree. `lock_reporting_state` keeps its caller at
:116, so `main`'s protection for the capture test is intact and nothing is dead.
@M3gA-Mind

Copy link
Copy Markdown
Collaborator

@senamakel @YellowSnnowmann — merged current main (824d3281c) into the branch to clear the conflict. One commit, 77d1e1013, and nothing of either of yours was dropped. New head 77d1e1013 (was 1c402c875); the PR is MERGEABLE again.

The one conflict, and why it resolved the way it did

tests/observability_wallet_expected_e2e.rs — both sides had fixed the same Sentry-hub cross-talk, by different routes:

  • main (d0509bb17) widened mod paging's private lock into one file-wide REPORTING_STATE_LOCK, taken both in the capture test and inside captured_events_for, serialising around a client bound on the process-global hub.
  • this branch (7d68ffe13, @YellowSnnowmann) removed the binding instead: captured_events_for binds its client to a private hub current only inside sentry::Hub::run, so no process-global state is touched at all.

Kept both. main's REPORTING_STATE_LOCK, lock_reporting_state() and its use in the capture test (:116) all survive untouched — that is the half protecting reporting_a_genuine_wallet_failure_still_emits_error, and it is still doing its job. The private-hub captured_events_for also survives.

The only line dropped is let _guard = lock_reporting_state(); inside captured_events_for, and only because on the merged tree there is no longer any sentry::init in that function for it to guard — its own comment ("sentry::init below binds a client to the process-global Hub") would describe code that no longer exists, and the function's doc comment two lines above says in as many words that no serialisation is needed. lock_reporting_state keeps its caller, so nothing is dead.

I nearly got this wrong: my first attempt used git checkout --ours, which takes the whole file and silently discarded REPORTING_STATE_LOCK and the :116 call. Caught it, redid it as a hunk-level resolution, and verified both sides are present.

Verified, not assumed — the resolution changes concurrency behaviour, so I ran the file the way the flake shows up:

3 × parallel  →  8 passed, 0 failed   (each run)
1 × serial    →  8 passed, 0 failed

Two things for you, neither of which I have touched

1. AI attribution — this is a merge blocker and it will stop the PR. Nine commits carry it:

1c402c875  ffb89c8a3  7d68ffe13  9097699a5  1eec16fef
f456863fc  b83d1aef5  145c7a452  06c69ad4a

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012jp2WUNkZ8dxDVmDtu3JxX

@senamakel's 15 commits are clean and so are mine — it is only these nine. Stripping them means rewriting your own commits' messages, so I have deliberately not done it on your behalf: it rewrites history you authored and changes every SHA after it. @YellowSnnowmann, it is a git rebase with a --msg-filter, or a re-commit of those nine; say the word and I will do it, but I would rather you drove a rewrite of your own commits.

2. #5979 noted, not re-derived. Thanks for the -c diff.external= diagnosis — 30ba798fe (--no-ext-diff) is on main and in this merge, so if that raw-coverage failure reappears it is inherited, not new.

I have not approved this PR. Gate 5 (no AI attribution) fails on the nine commits above; everything else I can check is in good shape. Once those are stripped and CI reports on the new head, it should be approvable.

…he private hub

My merge resolution in 77d1e10 dropped `lock_reporting_state()` from
`captured_events_for`, reasoning that a client bound to a private hub touches no
process-global state so the guard was redundant. CI disproved it:
`reporting_a_genuine_wallet_failure_still_emits_error` failed on the merge head
with an empty capture — exactly the failure d0509bb added the file-wide lock
to close.

The private hub removes the *client* binding; it does not remove the need to
serialise. `sentry-tracing`'s layer lives in the global subscriber stack, so
while a client is current on the paging thread a `tracing::error!` raised by
`capture_reporting` on another thread can be consumed by that layer instead of
reaching its fmt subscriber.

Both fixes are needed and both are now present: the private hub for the paging
envelope count, main's file-wide lock for the capture. The doc comment that
claimed no serialisation was needed is corrected rather than left contradicting
the code.

Worth recording that six green local runs preceded the CI failure, and six more
followed this fix — for a race, a passing run is weak evidence either way. The
reason to trust this one is that it restores the configuration d0509bb already
validated, not the run count.
@M3gA-Mind

Copy link
Copy Markdown
Collaborator

Correction to my previous comment — I got the conflict resolution wrong, CI caught it, and 0fdc3f398 fixes it.

77d1e1013 dropped lock_reporting_state() from captured_events_for, on the reasoning that @YellowSnnowmann's private hub touches no process-global state so main's guard was redundant there. Rust Core Coverage on that head then failed exactly where main said it would:

reporting_a_genuine_wallet_failure_still_emits_error ... FAILED
tests/observability_wallet_expected_e2e.rs:249

— the empty capture that d0509bb17 added the file-wide lock to close.

Why my reasoning was wrong. The private hub removes the client binding; it does not remove the need to serialise. sentry-tracing's layer lives in the global subscriber stack, so while a client is current on the paging thread, a tracing::error! raised by capture_reporting on another thread can be consumed by that layer instead of reaching its fmt subscriber. @YellowSnnowmann's fix addresses the paging envelope count; main's lock addresses the capture. They are two different failures and both fixes are needed.

0fdc3f398 restores the guard and corrects the doc comment that claimed no serialisation was needed, so the comment no longer contradicts the code. Nothing else changed; the private hub is untouched.

The uncomfortable part, recorded because it matters more than the fix: I ran that file 3× parallel + 1× serial before pushing and got 8/8 green every time. Six further runs after the fix are also green. For a race, a passing run is weak evidence in either direction — the reason to trust the current state is that it restores the configuration d0509bb17 had already validated, not the run count. I should have treated "I removed a lock and the tests still pass" as unproven rather than verified, and I have said so in the commit message.

Everything else from my previous comment stands: both authors' work is preserved, and the nine commits carrying Co-Authored-By: Claude Fable 5.1 / Claude-Session: are still the merge blocker I have not touched.

@M3gA-Mind
M3gA-Mind merged commit 9b14d06 into tinyhumansai:main Sep 3, 2026
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants