Skip to content

fix: align coding agent trace scopes#130

Open
willkill07 wants to merge 4 commits into
NVIDIA:mainfrom
willkill07:wkk_improve-claude-and-codex-traces
Open

fix: align coding agent trace scopes#130
willkill07 wants to merge 4 commits into
NVIDIA:mainfrom
willkill07:wkk_improve-claude-and-codex-traces

Conversation

@willkill07
Copy link
Copy Markdown
Member

@willkill07 willkill07 commented May 19, 2026

Overview

This PR improves CLI trace alignment for coding-agent runs so LLM and tool spans correlate to the correct turn, agent, or subagent scope across Codex and Claude Code flows. It keeps long-lived harness sessions as metadata, represents user-visible work as bounded turn agent scopes, and moves provider-specific correlation into alignment modules so session and gateway code can stay focused on lifecycle orchestration.

  • I confirm this contribution is my own work, or I have the right to submit it under this project's license.
  • I searched existing issues and open pull requests, and this does not duplicate existing work.

Details

  • Adds crates/cli/src/alignment/ as the owner for Codex- and Claude Code-specific trace alignment, session id recovery, subagent aliasing, Codex ChatGPT OAuth gateway normalization, and reusable metadata helpers.
  • Updates session lifecycle handling so Codex and Claude Code SessionStart events record metadata but do not emit long-lived top-level session spans.
  • Represents user-visible work as provider-named turn agent scopes such as codex-turn, claude-code-turn, and hermes-turn, with nemo_flow_scope_role, turn_index, and harness metadata for filtering.
  • Keeps subagent starts as agent scopes under the active turn, including Codex child-thread promotion and Claude Code Agent tool completion cleanup.
  • Adds aggressive idle cleanup for open turn scopes while preserving active gateway calls, hook-observed LLMs/tools, and subagent cleanup ordering.
  • Updates LLM/tool correlation with alias routing, sticky owner recovery, request-affinity matching, final root-owned turn output, and streamed final-response handoff.
  • Updates gateway code so provider-specific session/auth/upstream fallbacks are delegated to alignment while the gateway keeps generic request preparation and provider forwarding logic.
  • Updates ATIF trajectory dispatch so top-level agent scopes remain the trajectory roots now that turns are modeled as agent scopes.
  • Adds out-of-source coverage tests under crates/cli/tests/coverage for alignment adapters, gateway identifier/auth behavior, session aliasing, subagent cleanup, turn naming/output, idle cleanup, streaming final responses, and LLM/tool correlation.
  • Validation performed:
    • cargo fmt --all -- --check
    • git diff --check
    • cargo test -p nemo-flow-cli
    • cargo test -p nemo-flow observability
    • cargo clippy -p nemo-flow-cli --all-targets -- -D warnings
    • commit pre-commit hooks, including cargo fmt, cargo clippy, and cargo check

Where should the reviewer start?

  • Start with crates/cli/src/alignment/mod.rs to understand the boundary: alignment owns provider-specific interpretation and cross-session alias state, while session/gateway code stays generic.
  • Then read crates/cli/src/session.rs by lifecycle path rather than linearly: start_agent, open_turn, start_subagent, close_turn, resolve_llm_owner, and resolve_tool_owner are the main decisions.
  • Review the turn design strategically first: Codex and Claude Code sessions no longer create top-level session spans; turn scopes are provider-named agent scopes; subagent scopes still emit under the active turn.
  • Review crates/cli/src/alignment/codex.rs and crates/cli/src/alignment/claude_code.rs as adapters. Codex covers child-thread parentage and ChatGPT OAuth gateway normalization; Claude Code covers native session headers and Agent tool completion.
  • Review crates/cli/src/gateway.rs for the managed execution handoff, especially streaming final-response capture and active-call accounting for idle cleanup.
  • Use tests as executable examples before reading every implementation detail: alignment_tests.rs for generic alias routing, alignment_codex_tests.rs / alignment_claude_code_tests.rs for adapter-specific behavior, session_tests.rs for turn/subagent ownership, and gateway_tests.rs for gateway handoff cases.
  • To avoid getting overwhelmed, verify the PR in layers: provider adapter extraction first, turn scope lifecycle second, subagent alias promotion third, LLM/tool owner metadata fourth, and gateway auth/session fallback last.
  • The highest-risk behavior to scrutinize is whether child sessions are promoted only while empty, whether aliases clear on child or parent terminal events, and whether ambiguous tool/LLM calls fall back to the turn instead of guessing a subagent.

Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

  • Relates to: none

Summary by CodeRabbit

  • New Features

    • Provider-aware session alignment and subagent correlation for Claude Code and Codex, including improved session/subagent id handling and promotion.
  • Refactor

    • Centralized gateway URL/header/auth normalization and session-id resolution into a shared alignment layer.
    • Session lifecycle and subagent scope handling reworked for concurrent/out-of-order scenarios.
  • Tests

    • Extensive new coverage for gateway routing, alignment, subagent promotion, metadata extraction, and session lifecycle.
  • Other

    • Server now runs background idle-session sweeper; observability docs updated for trajectory scopes; prompt-submission event handling adjusted.

Signed-off-by: Will Killian <wkillian@nvidia.com>
@willkill07 willkill07 requested a review from a team as a code owner May 19, 2026 13:32
@github-actions github-actions Bot added size:XL PR is extra large Bug issue describes bug; PR fixes bug labels May 19, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Walkthrough

Adds an alignment subsystem that normalizes gateway requests, extracts and aliases session IDs, promotes child sessions into parent-owned subagents, and propagates correlation metadata through LLM and tool lifecycles.

Changes

Cross-Session Alignment and Subagent Promotion

Layer / File(s) Summary
Alignment module foundation and primitives
crates/cli/src/alignment/mod.rs
Defines SubagentSessionContext, GatewayRouteKind, SessionAlias, PendingSubagentStart, and SessionAlignmentState; implements route_event_through_alias, gateway helpers (gateway_session_id, gateway_upstream_url_override, gateway_forward_headers, gateway_subagent_id, gateway_identifier, agent_kind_for_gateway_provider), and JSON/metadata utilities (json_string_at, json_value_at, insert_optional, merge_metadata).
Claude Code alignment adapter
crates/cli/src/alignment/claude_code.rs, crates/cli/tests/coverage/alignment_claude_code_tests.rs
Adds owns_gateway_provider for Anthropic gateway detection, session_id_from_headers to read x-claude-code-session-id, and completed_subagent_from_agent_tool to detect tool-driven subagent completion; includes tests for ownership, header extraction, and result-key variants.
Codex alignment adapter
crates/cli/src/alignment/codex.rs, crates/cli/tests/coverage/alignment_codex_tests.rs
Implements Codex-specific logic: SubagentContext, prompt_cache_session_id, ChatGPT upstream/authorization handling (chatgpt_upstream_url_if_needed, strip_chatgpt_oauth_for_openai_route), subagent context discovery (payload/transcript fallback), metadata augmentation, subagent-event rewriting, alias_for_child_session, and llm_owner_metadata; includes tests for conditionals and transcript fallback.
Gateway integration and header/URL delegation
crates/cli/src/gateway.rs, crates/cli/tests/coverage/gateway_tests.rs, crates/cli/src/main.rs
Delegates upstream URL and header normalization to alignment; adds ProviderRoute::alignment_route() mapping; build_llm_gateway_start resolves session id using gateway_session_id(headers, body, route); forwarding uses gateway_forward_headers(...); tests updated for mapping, start construction, header/JWT handling, and ChatGPT backend override behavior.
Session manager alignment and subagent promotion
crates/cli/src/session.rs (SessionManager)
SessionManager adds shared SessionAlignmentState, routes apply_events through alignment, promotes pending subagent starts on parent AgentStarted, resolves start aliases for gateway calls, and infers agent kind using alignment mappings.
Session lifecycle and subagent scope management
crates/cli/src/session.rs (Session)
Session now tracks per-subagent scope stacks and completed_subagents; refactors subagent start/end to async with close_subagent_scope; updates hook/tool LLM ownership handling, request-affinity ownership, and propagates correlation metadata (LlmOwnerResolution.metadata).
Alignment core and integration tests
crates/cli/tests/coverage/alignment_tests.rs
Tests gateway session id precedence, gateway_subagent_id/gateway_identifier precedence and JSON-path extraction, agent_kind_for_gateway_provider, request_affinity_key, route_event_through_alias rewrite behavior across event variants, and JSON/metadata utility edge cases.
Codex and gateway tests
crates/cli/tests/coverage/alignment_codex_tests.rs, crates/cli/tests/coverage/gateway_tests.rs
Codex tests cover prompt-cache session id, ChatGPT upstream override and header stripping, subagent context/transcript fallback, and llm_owner_metadata. Gateway tests assert alignment route mappings, build_llm_gateway_start behavior, header/JWT handling, and ChatGPT backend override edge cases.
Session manager integration and correlation tests
crates/cli/tests/coverage/session_tests.rs
Extensive async tests for alias creation/removal, pending promotion, LLM parent-owner routing (explicit, hinted, sticky), request-affinity behavior, and ordering/cleanup edge-cases.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Gateway
  participant Alignment
  participant SessionManager
  participant Session
  Client->>Gateway: HTTP gateway request (headers, body)
  Gateway->>Alignment: gateway_session_id(headers, body, route)
  Alignment->>Gateway: resolved session_id
  Gateway->>SessionManager: build_llm_gateway_start(resolved session_id, metadata)
  SessionManager->>Alignment: route_event_through_alias(NormalizedEvent)
  Alignment->>SessionManager: rewritten event (parent session id, alias_metadata)
  SessionManager->>Session: apply_event_to_session(rewritten event)
  Session->>Session: start_subagent / end_subagent (async) / close_subagent_scope
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title follows Conventional Commits format with type 'fix', lowercase, clear imperative summary under 72 characters, and no trailing period.
Description check ✅ Passed The description includes all required template sections: overview with checked confirmations, detailed explanation of changes, reviewer guidance with strategic starting points, and related issues reference.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@github-actions github-actions Bot added the lang:rust PR changes/introduces Rust code label May 19, 2026
@willkill07 willkill07 self-assigned this May 19, 2026
@willkill07 willkill07 added this to the 0.3 milestone May 19, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/cli/src/alignment/codex.rs`:
- Around line 134-145: subagent_context_from_transcript performs blocking file
I/O (std::fs::File::open and BufReader::read_line) which can block an async
executor; change this to an async-safe approach by either (A) making
subagent_context_from_transcript async and using tokio::fs::File::open +
tokio::io::BufReader::read_line to read the single line asynchronously, then
parse and call subagent_context_from_value, or (B) keep it sync but wrap the
blocking work in tokio::task::spawn_blocking (move the File open + read_line +
serde_json::from_str into the spawned closure) and await the result, ensuring
callers in async contexts don't block the runtime.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: b939f4b0-94da-45ec-a9d8-19444e256210

📥 Commits

Reviewing files that changed from the base of the PR and between 91ca10c and 5a418e0.

📒 Files selected for processing (11)
  • crates/cli/src/alignment/claude_code.rs
  • crates/cli/src/alignment/codex.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/gateway.rs
  • crates/cli/src/main.rs
  • crates/cli/src/session.rs
  • crates/cli/tests/coverage/alignment_claude_code_tests.rs
  • crates/cli/tests/coverage/alignment_codex_tests.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (7)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-ffi-surface/SKILL.md)

**/*.rs: Run cargo fmt --all for FFI work as it is Rust work
Run just test-rust for FFI validation
Run cargo clippy --workspace --all-targets -- -D warnings to enforce warnings-as-errors linting

**/*.rs: Run cargo fmt --all for Rust code formatting
Run cargo clippy --workspace --all-targets -- -D warnings to enforce Rust linting with no warnings
Run just test-rust as the shared-runtime build/test wrapper for Rust changes

Use Rust snake_case naming convention for Rust code

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for Rust code formatting when Node changes touch Rust files
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting when Rust files changed as part of Node work

**/*.rs: Always run just test-rust when any Rust code changes
Always run cargo fmt --all when any Rust code changes
Always run cargo clippy --workspace --all-targets -- -D warnings when any Rust code changes

If any Rust files changed as part of the Python work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

Files:

  • crates/cli/src/main.rs
  • crates/cli/src/alignment/claude_code.rs
  • crates/cli/tests/coverage/alignment_claude_code_tests.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/alignment_codex_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/cli/src/gateway.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/alignment/codex.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/cli/src/session.rs
**/*.{rs,go,js,ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Include SPDX license headers in all Rust, Go, JavaScript, and TypeScript source files using C-style comment syntax

Files:

  • crates/cli/src/main.rs
  • crates/cli/src/alignment/claude_code.rs
  • crates/cli/tests/coverage/alignment_claude_code_tests.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/alignment_codex_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/cli/src/gateway.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/alignment/codex.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/cli/src/session.rs
**/*.{rs,py,go,js,ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use SONAR_IGNORE_START / SONAR_IGNORE_END markers only for documented false positives that cannot be resolved in code; keep ignored blocks small, add explanatory comments, and require reviewer sign-off

Files:

  • crates/cli/src/main.rs
  • crates/cli/src/alignment/claude_code.rs
  • crates/cli/tests/coverage/alignment_claude_code_tests.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/alignment_codex_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/cli/src/gateway.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/alignment/codex.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/cli/src/session.rs
**/*.{js,ts,tsx,jsx,py,rs,go,java,c,cpp,h,cc,cxx,cs,rb,php,swift,kt}

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changed files must be formatted with the language-native formatter

Files:

  • crates/cli/src/main.rs
  • crates/cli/src/alignment/claude_code.rs
  • crates/cli/tests/coverage/alignment_claude_code_tests.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/alignment_codex_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/cli/src/gateway.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/alignment/codex.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/cli/src/session.rs
**/*.{py,js,ts,tsx,go,rs,md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Format changed files with the language-native formatter before the final lint/test pass

Files:

  • crates/cli/src/main.rs
  • crates/cli/src/alignment/claude_code.rs
  • crates/cli/tests/coverage/alignment_claude_code_tests.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/alignment_codex_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/cli/src/gateway.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/alignment/codex.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/cli/src/session.rs
**/*.{rs,py,js,ts,tsx,go}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

During iteration, prefer uv run pre-commit run --files <changed files...> for targeted validation

Files:

  • crates/cli/src/main.rs
  • crates/cli/src/alignment/claude_code.rs
  • crates/cli/tests/coverage/alignment_claude_code_tests.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/alignment_codex_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/cli/src/gateway.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/alignment/codex.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/cli/src/session.rs
{crates/**/tests/**,python/tests/**,go/nemo_flow/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_flow/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/cli/tests/coverage/alignment_claude_code_tests.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/alignment_codex_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/cli/tests/coverage/gateway_tests.rs
🔇 Additional comments (47)
crates/cli/src/alignment/mod.rs (8)

1-20: LGTM!


21-96: LGTM!


98-192: LGTM!


194-266: LGTM!


268-349: LGTM!


351-479: LGTM!


481-536: LGTM!


538-541: LGTM!

crates/cli/src/alignment/claude_code.rs (1)

1-50: LGTM!

crates/cli/tests/coverage/alignment_claude_code_tests.rs (1)

1-91: LGTM!

crates/cli/src/alignment/codex.rs (2)

147-245: LGTM!


247-339: LGTM!

crates/cli/tests/coverage/alignment_codex_tests.rs (1)

1-269: LGTM!

crates/cli/tests/coverage/alignment_tests.rs (1)

1-317: LGTM!

crates/cli/src/main.rs (1)

7-7: LGTM!

crates/cli/src/gateway.rs (6)

26-26: LGTM!

Also applies to: 90-91


108-165: LGTM!


549-569: LGTM!


694-709: LGTM!


776-789: LGTM!


802-877: LGTM!

crates/cli/tests/coverage/gateway_tests.rs (1)

5-5: LGTM!

Also applies to: 82-101, 167-170, 175-178, 185-185, 194-194, 198-222, 275-319, 347-351, 364-368, 385-389, 403-407, 496-506, 516-535, 545-553, 558-562, 565-572, 575-583, 586-594

crates/cli/src/session.rs (16)

4-4: LGTM!

Also applies to: 12-14, 25-28, 40-46, 100-106


136-168: LGTM!


170-177: LGTM!


187-265: LGTM!


280-339: LGTM!


391-412: LGTM!


414-464: LGTM!


467-508: LGTM!


510-597: LGTM!


614-651: LGTM!


653-677: LGTM!


679-765: LGTM!


806-881: LGTM!


905-1016: LGTM!


1032-1108: LGTM!

Also applies to: 1149-1206


1239-1456: LGTM!

crates/cli/tests/coverage/session_tests.rs (9)

69-83: LGTM!


159-199: LGTM!


201-286: LGTM!


288-351: LGTM!


353-525: LGTM!


527-656: LGTM!


658-964: LGTM!


2087-2367: LGTM!


3015-3023: LGTM!

Comment on lines +134 to +145
fn subagent_context_from_transcript(event: &SessionEvent) -> Option<SubagentContext> {
let transcript_path = json_string_at(&event.metadata, &[&["transcript_path"][..]])
.or_else(|| json_string_at(&event.payload, &[&["transcript_path"][..]]))?;
let file = std::fs::File::open(transcript_path).ok()?;
let mut reader = BufReader::new(file);
let mut line = String::new();
if reader.read_line(&mut line).ok()? == 0 {
return None;
}
let value = serde_json::from_str::<Value>(&line).ok()?;
subagent_context_from_value(&value)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Blocking file I/O on async path.

subagent_context_from_transcript performs synchronous std::fs::File::open and BufReader::read_line. If called from an async context (e.g., within a Tokio runtime), this blocks the executor thread.

Given the comment that this reads only one line and is a fallback path, the impact is likely low in practice. Consider using tokio::fs or spawn_blocking if profiling shows contention.

🤖 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 `@crates/cli/src/alignment/codex.rs` around lines 134 - 145,
subagent_context_from_transcript performs blocking file I/O (std::fs::File::open
and BufReader::read_line) which can block an async executor; change this to an
async-safe approach by either (A) making subagent_context_from_transcript async
and using tokio::fs::File::open + tokio::io::BufReader::read_line to read the
single line asynchronously, then parse and call subagent_context_from_value, or
(B) keep it sync but wrap the blocking work in tokio::task::spawn_blocking (move
the File open + read_line + serde_json::from_str into the spawned closure) and
await the result, ensuring callers in async contexts don't block the runtime.

Signed-off-by: Will Killian <wkillian@nvidia.com>
@github-actions github-actions Bot added size:XXL PR is very large and removed size:XL PR is extra large labels May 19, 2026
Signed-off-by: Will Killian <wkillian@nvidia.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/cli/src/session.rs`:
- Around line 1829-1838: The helper llm_status_teaches_request_affinity
currently treats the "subagent_start" status as teaching request affinity;
remove "subagent_start" from the match arm so only high-confidence statuses
(explicit, single_hint, matched_hint, active_subagent, request_affinity) return
true. Update llm_status_teaches_request_affinity to exclude the "subagent_start"
variant and leave record_llm_request_affinity / llm_request_affinity behavior
unchanged so the weak heuristic does not promote a low-confidence guess into
stable request affinity.
- Around line 474-479: The apply_start_alias function currently sets
start.session_id from alias but only fills start.subagent_id when None; change
it so the alias's subagent wins: in apply_start_alias (operating on
LlmGatewayStart and SessionAlias) always set start.subagent_id =
Some(alias.subagent_id.clone()) (or alternatively return an error on mismatch if
you prefer rejection semantics) so the alias-owned subagent becomes
authoritative and cannot be accidentally left as a conflicting existing subagent
id.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 4eae72c1-5d72-4bc6-8d22-42ad69481ffd

📥 Commits

Reviewing files that changed from the base of the PR and between 5a418e0 and 01e380c.

📒 Files selected for processing (4)
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (7)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-ffi-surface/SKILL.md)

**/*.rs: Run cargo fmt --all for FFI work as it is Rust work
Run just test-rust for FFI validation
Run cargo clippy --workspace --all-targets -- -D warnings to enforce warnings-as-errors linting

**/*.rs: Run cargo fmt --all for Rust code formatting
Run cargo clippy --workspace --all-targets -- -D warnings to enforce Rust linting with no warnings
Run just test-rust as the shared-runtime build/test wrapper for Rust changes

Use Rust snake_case naming convention for Rust code

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for Rust code formatting when Node changes touch Rust files
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting when Rust files changed as part of Node work

**/*.rs: Always run just test-rust when any Rust code changes
Always run cargo fmt --all when any Rust code changes
Always run cargo clippy --workspace --all-targets -- -D warnings when any Rust code changes

If any Rust files changed as part of the Python work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

Files:

  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
**/*.{rs,go,js,ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Include SPDX license headers in all Rust, Go, JavaScript, and TypeScript source files using C-style comment syntax

Files:

  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
**/*.{rs,py,go,js,ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use SONAR_IGNORE_START / SONAR_IGNORE_END markers only for documented false positives that cannot be resolved in code; keep ignored blocks small, add explanatory comments, and require reviewer sign-off

Files:

  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
**/*.{js,ts,tsx,jsx,py,rs,go,java,c,cpp,h,cc,cxx,cs,rb,php,swift,kt}

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changed files must be formatted with the language-native formatter

Files:

  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
**/*.{py,js,ts,tsx,go,rs,md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Format changed files with the language-native formatter before the final lint/test pass

Files:

  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
**/*.{rs,py,js,ts,tsx,go}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

During iteration, prefer uv run pre-commit run --files <changed files...> for targeted validation

Files:

  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
{crates/**/tests/**,python/tests/**,go/nemo_flow/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_flow/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
🔇 Additional comments (3)
crates/cli/tests/coverage/alignment_tests.rs (1)

1-352: LGTM!

crates/cli/tests/coverage/session_tests.rs (1)

1-3209: LGTM!

crates/cli/src/alignment/mod.rs (1)

1-601: LGTM!

Comment thread crates/cli/src/session.rs
Comment on lines +474 to +479
fn apply_start_alias(start: &mut LlmGatewayStart, alias: &SessionAlias) {
start.session_id = Some(alias.parent_session_id.clone());
if start.subagent_id.is_none() {
start.subagent_id = Some(alias.subagent_id.clone());
}
start.metadata = merge_metadata(start.metadata.clone(), alias.metadata());
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make the alias-owned subagent authoritative.

When start.session_id is rewritten through a child-session alias, preserving a conflicting start.subagent_id can attach that child thread's LLM to the wrong sibling scope in the parent session. The alias is the stronger ownership signal here, so this should overwrite or reject conflicting subagent ids instead of only filling None.

Suggested fix
 fn apply_start_alias(start: &mut LlmGatewayStart, alias: &SessionAlias) {
     start.session_id = Some(alias.parent_session_id.clone());
-    if start.subagent_id.is_none() {
-        start.subagent_id = Some(alias.subagent_id.clone());
-    }
+    start.subagent_id = Some(alias.subagent_id.clone());
     start.metadata = merge_metadata(start.metadata.clone(), alias.metadata());
 }
🤖 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 `@crates/cli/src/session.rs` around lines 474 - 479, The apply_start_alias
function currently sets start.session_id from alias but only fills
start.subagent_id when None; change it so the alias's subagent wins: in
apply_start_alias (operating on LlmGatewayStart and SessionAlias) always set
start.subagent_id = Some(alias.subagent_id.clone()) (or alternatively return an
error on mismatch if you prefer rejection semantics) so the alias-owned subagent
becomes authoritative and cannot be accidentally left as a conflicting existing
subagent id.

Comment thread crates/cli/src/session.rs Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
crates/cli/src/session.rs (2)

1829-1838: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't persist request affinity from subagent_start.

set_last_subagent_start_owner is documented as a weak heuristic, but this helper turns "subagent_start" into durable llm_request_affinity. That can lock later requests onto the wrong worker from a guess instead of a high-confidence signal.

Suggested fix
 fn owner_status_teaches_request_affinity(status: &str) -> bool {
     matches!(
         status,
         "explicit"
             | "single_hint"
             | "matched_hint"
             | "active_subagent"
-            | "subagent_start"
             | "request_affinity"
     )
 }
🤖 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 `@crates/cli/src/session.rs` around lines 1829 - 1838, The helper
owner_status_teaches_request_affinity currently treats "subagent_start" as a
status that should persist request affinity; remove "subagent_start" from the
matches so it no longer yields true. Update the match arms in
owner_status_teaches_request_affinity to exclude "subagent_start" (keep the
other statuses: "explicit", "single_hint", "matched_hint", "active_subagent",
"request_affinity") and add/adjust any unit tests or comments referring to
persisting affinity from subagent_start accordingly.

474-479: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make the alias-owned subagent authoritative.

Once Line 475 rewrites the LLM start onto the parent session, preserving an existing start.subagent_id can still attach the call and its later response hints to the wrong sibling scope. The alias is the stronger ownership signal here, so it should overwrite any conflicting subagent id.

Suggested fix
 fn apply_start_alias(start: &mut LlmGatewayStart, alias: &SessionAlias) {
     start.session_id = Some(alias.parent_session_id.clone());
-    if start.subagent_id.is_none() {
-        start.subagent_id = Some(alias.subagent_id.clone());
-    }
+    start.subagent_id = Some(alias.subagent_id.clone());
     start.metadata = merge_metadata(start.metadata.clone(), alias.metadata());
 }
🤖 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 `@crates/cli/src/session.rs` around lines 474 - 479, In apply_start_alias
replace the conditional preservation of start.subagent_id so the alias-owned
subagent becomes authoritative: always assign start.subagent_id =
Some(alias.subagent_id.clone()) (instead of only when None) after copying the
parent session id; keep the existing session id assignment and metadata merge
(LlmGatewayStart, SessionAlias, start.subagent_id, alias.subagent_id,
merge_metadata).
🤖 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.

Duplicate comments:
In `@crates/cli/src/session.rs`:
- Around line 1829-1838: The helper owner_status_teaches_request_affinity
currently treats "subagent_start" as a status that should persist request
affinity; remove "subagent_start" from the matches so it no longer yields true.
Update the match arms in owner_status_teaches_request_affinity to exclude
"subagent_start" (keep the other statuses: "explicit", "single_hint",
"matched_hint", "active_subagent", "request_affinity") and add/adjust any unit
tests or comments referring to persisting affinity from subagent_start
accordingly.
- Around line 474-479: In apply_start_alias replace the conditional preservation
of start.subagent_id so the alias-owned subagent becomes authoritative: always
assign start.subagent_id = Some(alias.subagent_id.clone()) (instead of only when
None) after copying the parent session id; keep the existing session id
assignment and metadata merge (LlmGatewayStart, SessionAlias, start.subagent_id,
alias.subagent_id, merge_metadata).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 1632ae85-1d84-492f-98ff-cf3ef67fb7ce

📥 Commits

Reviewing files that changed from the base of the PR and between 01e380c and 7a3228e.

📒 Files selected for processing (4)
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Rust / Test (linux-amd64)
  • GitHub Check: Rust / Test (linux-arm64)
  • GitHub Check: Rust / Test (windows-arm64)
  • GitHub Check: Rust / Test (macos-arm64)
  • GitHub Check: Rust / Test (windows-amd64)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-ffi-surface/SKILL.md)

**/*.rs: Run cargo fmt --all for FFI work as it is Rust work
Run just test-rust for FFI validation
Run cargo clippy --workspace --all-targets -- -D warnings to enforce warnings-as-errors linting

**/*.rs: Run cargo fmt --all for Rust code formatting
Run cargo clippy --workspace --all-targets -- -D warnings to enforce Rust linting with no warnings
Run just test-rust as the shared-runtime build/test wrapper for Rust changes

Use Rust snake_case naming convention for Rust code

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for Rust code formatting when Node changes touch Rust files
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting when Rust files changed as part of Node work

**/*.rs: Always run just test-rust when any Rust code changes
Always run cargo fmt --all when any Rust code changes
Always run cargo clippy --workspace --all-targets -- -D warnings when any Rust code changes

If any Rust files changed as part of the Python work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

Files:

  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
**/*.{rs,go,js,ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Include SPDX license headers in all Rust, Go, JavaScript, and TypeScript source files using C-style comment syntax

Files:

  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
**/*.{rs,py,go,js,ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use SONAR_IGNORE_START / SONAR_IGNORE_END markers only for documented false positives that cannot be resolved in code; keep ignored blocks small, add explanatory comments, and require reviewer sign-off

Files:

  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
**/*.{js,ts,tsx,jsx,py,rs,go,java,c,cpp,h,cc,cxx,cs,rb,php,swift,kt}

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changed files must be formatted with the language-native formatter

Files:

  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
**/*.{py,js,ts,tsx,go,rs,md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Format changed files with the language-native formatter before the final lint/test pass

Files:

  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
**/*.{rs,py,js,ts,tsx,go}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

During iteration, prefer uv run pre-commit run --files <changed files...> for targeted validation

Files:

  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
{crates/**/tests/**,python/tests/**,go/nemo_flow/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_flow/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/cli/tests/coverage/alignment_tests.rs

Signed-off-by: Will Killian <wkillian@nvidia.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
crates/cli/src/session.rs (2)

2159-2169: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't teach request affinity from subagent_start.

subagent_start is documented as a weak heuristic (lines 1789-1800), but owner_status_teaches_request_affinity promotes it into stable llm_request_affinity. This can lock later matching requests onto the wrong worker from a guess, defeating the "high-confidence only" intent in record_llm_request_affinity.

Suggested fix
 fn owner_status_teaches_request_affinity(status: &str) -> bool {
     matches!(
         status,
         "explicit"
             | "single_hint"
             | "matched_hint"
             | "active_subagent"
-            | "subagent_start"
             | "request_affinity"
     )
 }
🤖 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 `@crates/cli/src/session.rs` around lines 2159 - 2169, The function
owner_status_teaches_request_affinity currently treats "subagent_start" as a
status that promotes stable llm_request_affinity; remove "subagent_start" from
owner_status_teaches_request_affinity so that only high-confidence statuses
(explicit, single_hint, matched_hint, active_subagent, request_affinity) return
true, and update any related tests or callers (e.g.,
record_llm_request_affinity) to rely on the revised predicate; ensure no other
code still assumes subagent_start implies stable affinity.

538-543: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make the alias-owned subagent authoritative.

When start.session_id is rewritten through a child-session alias, preserving a conflicting start.subagent_id can attach that child thread's LLM to the wrong sibling scope in the parent session. The alias is the stronger ownership signal here, so this should overwrite or reject conflicting subagent ids instead of only filling None.

Suggested fix
 fn apply_start_alias(start: &mut LlmGatewayStart, alias: &SessionAlias) {
     start.session_id = Some(alias.parent_session_id.clone());
-    if start.subagent_id.is_none() {
-        start.subagent_id = Some(alias.subagent_id.clone());
-    }
+    start.subagent_id = Some(alias.subagent_id.clone());
     start.metadata = merge_metadata(start.metadata.clone(), alias.metadata());
 }
🤖 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 `@crates/cli/src/session.rs` around lines 538 - 543, In apply_start_alias, when
applying a child-session alias to an LlmGatewayStart, the alias's subagent must
be authoritative: always set start.subagent_id to
Some(alias.subagent_id.clone()) instead of only assigning when
start.subagent_id.is_none(); keep the existing start.session_id assignment and
metadata merge (merge_metadata(start.metadata.clone(), alias.metadata())), but
replace the conditional subagent fill with an unconditional overwrite so the
alias's subagent cannot be left conflicting with the rewritten session_id.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/cli/src/alignment/mod.rs`:
- Line 22: REQUEST_AFFINITY_KEY_MAX_CHARS is defined but the 24-character
minimum inside request_affinity_key is a magic number; add a named constant
REQUEST_AFFINITY_KEY_MIN_CHARS = 24 and replace the hardcoded 24 in the
request_affinity_key function (and any other occurrences around the
request-affinity logic) with this constant to make the policy explicit and
consistent with REQUEST_AFFINITY_KEY_MAX_CHARS.
- Around line 584-589: The function looks_like_json_payload currently attempts
to parse the entire input with serde_json on every call; change it to first trim
leading whitespace and short-circuit non-JSON by returning false unless the
first non-whitespace char is '{' or '[' (indicating object/array), and only then
call serde_json::from_str::<Value> to confirm Ok(Value::Object(_) |
Value::Array(_)); update the function name and logic where needed to reference
looks_like_json_payload so callers get the optimized behavior.

---

Duplicate comments:
In `@crates/cli/src/session.rs`:
- Around line 2159-2169: The function owner_status_teaches_request_affinity
currently treats "subagent_start" as a status that promotes stable
llm_request_affinity; remove "subagent_start" from
owner_status_teaches_request_affinity so that only high-confidence statuses
(explicit, single_hint, matched_hint, active_subagent, request_affinity) return
true, and update any related tests or callers (e.g.,
record_llm_request_affinity) to rely on the revised predicate; ensure no other
code still assumes subagent_start implies stable affinity.
- Around line 538-543: In apply_start_alias, when applying a child-session alias
to an LlmGatewayStart, the alias's subagent must be authoritative: always set
start.subagent_id to Some(alias.subagent_id.clone()) instead of only assigning
when start.subagent_id.is_none(); keep the existing start.session_id assignment
and metadata merge (merge_metadata(start.metadata.clone(), alias.metadata())),
but replace the conditional subagent fill with an unconditional overwrite so the
alias's subagent cannot be left conflicting with the rewritten session_id.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 1c07dd5d-3a4a-4b05-86f7-e1741d04a55c

📥 Commits

Reviewing files that changed from the base of the PR and between 7a3228e and fbfa709.

📒 Files selected for processing (13)
  • crates/cli/src/adapters/mod.rs
  • crates/cli/src/alignment/claude_code.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/gateway.rs
  • crates/cli/src/server.rs
  • crates/cli/src/session.rs
  • crates/cli/tests/coverage/adapters_tests.rs
  • crates/cli/tests/coverage/alignment_claude_code_tests.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/cli/tests/coverage/server_tests.rs
  • crates/cli/tests/coverage/session_tests.rs
  • crates/core/src/observability/plugin_component.rs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Check / Run
🧰 Additional context used
📓 Path-based instructions (11)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-ffi-surface/SKILL.md)

**/*.rs: Run cargo fmt --all for FFI work as it is Rust work
Run just test-rust for FFI validation
Run cargo clippy --workspace --all-targets -- -D warnings to enforce warnings-as-errors linting

**/*.rs: Run cargo fmt --all for Rust code formatting
Run cargo clippy --workspace --all-targets -- -D warnings to enforce Rust linting with no warnings
Run just test-rust as the shared-runtime build/test wrapper for Rust changes

Use Rust snake_case naming convention for Rust code

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for Rust code formatting when Node changes touch Rust files
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting when Rust files changed as part of Node work

**/*.rs: Always run just test-rust when any Rust code changes
Always run cargo fmt --all when any Rust code changes
Always run cargo clippy --workspace --all-targets -- -D warnings when any Rust code changes

If any Rust files changed as part of the Python work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

Files:

  • crates/cli/src/server.rs
  • crates/cli/tests/coverage/adapters_tests.rs
  • crates/cli/tests/coverage/alignment_claude_code_tests.rs
  • crates/cli/tests/coverage/server_tests.rs
  • crates/cli/src/alignment/claude_code.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/cli/src/adapters/mod.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/cli/src/gateway.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
**/*.{rs,go,js,ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Include SPDX license headers in all Rust, Go, JavaScript, and TypeScript source files using C-style comment syntax

Files:

  • crates/cli/src/server.rs
  • crates/cli/tests/coverage/adapters_tests.rs
  • crates/cli/tests/coverage/alignment_claude_code_tests.rs
  • crates/cli/tests/coverage/server_tests.rs
  • crates/cli/src/alignment/claude_code.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/cli/src/adapters/mod.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/cli/src/gateway.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
**/*.{rs,py,go,js,ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use SONAR_IGNORE_START / SONAR_IGNORE_END markers only for documented false positives that cannot be resolved in code; keep ignored blocks small, add explanatory comments, and require reviewer sign-off

Files:

  • crates/cli/src/server.rs
  • crates/cli/tests/coverage/adapters_tests.rs
  • crates/cli/tests/coverage/alignment_claude_code_tests.rs
  • crates/cli/tests/coverage/server_tests.rs
  • crates/cli/src/alignment/claude_code.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/cli/src/adapters/mod.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/cli/src/gateway.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
**/*.{js,ts,tsx,jsx,py,rs,go,java,c,cpp,h,cc,cxx,cs,rb,php,swift,kt}

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changed files must be formatted with the language-native formatter

Files:

  • crates/cli/src/server.rs
  • crates/cli/tests/coverage/adapters_tests.rs
  • crates/cli/tests/coverage/alignment_claude_code_tests.rs
  • crates/cli/tests/coverage/server_tests.rs
  • crates/cli/src/alignment/claude_code.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/cli/src/adapters/mod.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/cli/src/gateway.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
**/*.{py,js,ts,tsx,go,rs,md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Format changed files with the language-native formatter before the final lint/test pass

Files:

  • crates/cli/src/server.rs
  • crates/cli/tests/coverage/adapters_tests.rs
  • crates/cli/tests/coverage/alignment_claude_code_tests.rs
  • crates/cli/tests/coverage/server_tests.rs
  • crates/cli/src/alignment/claude_code.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/cli/src/adapters/mod.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/cli/src/gateway.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
**/*.{rs,py,js,ts,tsx,go}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

During iteration, prefer uv run pre-commit run --files <changed files...> for targeted validation

Files:

  • crates/cli/src/server.rs
  • crates/cli/tests/coverage/adapters_tests.rs
  • crates/cli/tests/coverage/alignment_claude_code_tests.rs
  • crates/cli/tests/coverage/server_tests.rs
  • crates/cli/src/alignment/claude_code.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/cli/src/adapters/mod.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/cli/src/gateway.rs
  • crates/cli/tests/coverage/alignment_tests.rs
  • crates/cli/src/alignment/mod.rs
  • crates/cli/src/session.rs
{crates/**/tests/**,python/tests/**,go/nemo_flow/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_flow/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/cli/tests/coverage/adapters_tests.rs
  • crates/cli/tests/coverage/alignment_claude_code_tests.rs
  • crates/cli/tests/coverage/server_tests.rs
  • crates/cli/tests/coverage/gateway_tests.rs
  • crates/cli/tests/coverage/alignment_tests.rs
crates/{core,adaptive}/**

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/core/src/observability/plugin_component.rs
{crates/core,crates/adaptive}/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-wasm-binding/SKILL.md)

If the change touched shared runtime semantics in crates/core or crates/adaptive, also use validate-change

Files:

  • crates/core/src/observability/plugin_component.rs
crates/core/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

When crates/core changes, run the full validation matrix across Rust, Python, Go, Node.js, and WebAssembly

crates/core/**/*.rs: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths. Keep errors explicit and binding-appropriate at the wrapper layer.

Files:

  • crates/core/src/observability/plugin_component.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/core/src/observability/plugin_component.rs
🔇 Additional comments (20)
crates/cli/src/server.rs (1)

110-110: LGTM!

crates/cli/tests/coverage/adapters_tests.rs (1)

622-623: LGTM!

Also applies to: 639-644

crates/cli/tests/coverage/alignment_claude_code_tests.rs (1)

1-138: LGTM!

crates/cli/tests/coverage/server_tests.rs (1)

276-277: LGTM!

Also applies to: 306-309

crates/cli/src/alignment/claude_code.rs (1)

19-77: LGTM!

crates/core/src/observability/plugin_component.rs (1)

15-17: LGTM!

Also applies to: 174-179, 204-204, 646-647, 650-673, 862-875

crates/cli/src/adapters/mod.rs (1)

384-393: LGTM!

crates/cli/tests/coverage/gateway_tests.rs (1)

5-5: LGTM!

Also applies to: 8-8, 82-101, 167-170, 176-178, 185-185, 194-223, 275-319, 347-351, 364-368, 385-389, 403-407, 496-506, 516-535, 545-553, 558-562, 565-593, 669-720, 722-796, 798-801

crates/cli/src/gateway.rs (4)

562-631: LGTM!

The GatewayCallGuard correctly implements idempotent finalization: sessions.take() ensures either the async finish() path or the Drop path executes cleanup exactly once. The try_current().spawn() pattern in Drop is appropriate for fire-and-forget cleanup when the stream is dropped mid-flight.


878-891: LGTM!


904-979: LGTM!


26-31: LGTM!

Also applies to: 90-91, 117-117, 270-270, 283-286, 371-371, 375-384, 415-421, 427-434, 436-439, 530-560, 652-653, 662-662, 674-676, 796-798

crates/cli/tests/coverage/alignment_tests.rs (1)

1-538: LGTM!

Comprehensive test coverage for alignment behavior: gateway session/subagent/identifier precedence, agent kind inference, session scope policy, request affinity key extraction across provider formats, event routing through aliases, and JSON/metadata utilities.

crates/cli/src/alignment/mod.rs (1)

1-658: LGTM!

crates/cli/src/session.rs (6)

220-244: LGTM!

Idle sweeper correctly uses weak references to avoid extending manager lifetime, and skips sessions with active gateway calls, LLMs, or tools.


1204-1254: LGTM!

Per-subagent scope stacks correctly enable out-of-order parallel subagent completion without corrupting the task-local stack. The stack is seeded with parent scopes and stored separately.


1286-1326: LGTM!

close_subagent_scope correctly cleans up: removes from subagents/stacks, pops scope using the subagent's own stack, marks completed, and prunes tool hints and request affinity tied to the closed subagent.


1407-1424: LGTM!

hook_llm_owner correctly recovers subagent ownership from llm_correlation_subagent_id metadata stamped by alignment routing, keeping aliased child LLMs under the correct subagent scope.


1570-1646: LGTM!

LLM owner resolution correctly adds request_affinity_owner in the precedence chain between matched hints and sticky owner, with proper cleanup when the affinity-mapped subagent no longer exists.


4-4: LGTM!

Also applies to: 12-14, 25-28, 38-48, 96-126, 150-191, 749-814, 983-1059, 1149-1162, 1164-1170, 1776-1800, 1802-1830, 1848-1868

pub(crate) mod claude_code;
pub(crate) mod codex;

const REQUEST_AFFINITY_KEY_MAX_CHARS: usize = 4096;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Document the affinity key minimum length constant.

REQUEST_AFFINITY_KEY_MAX_CHARS is defined but the 24-character minimum in request_affinity_key is a magic number. Consider extracting it to a named constant (e.g., REQUEST_AFFINITY_KEY_MIN_CHARS) for consistency and to clarify the policy.

Also applies to: 373-378

🤖 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 `@crates/cli/src/alignment/mod.rs` at line 22, REQUEST_AFFINITY_KEY_MAX_CHARS
is defined but the 24-character minimum inside request_affinity_key is a magic
number; add a named constant REQUEST_AFFINITY_KEY_MIN_CHARS = 24 and replace the
hardcoded 24 in the request_affinity_key function (and any other occurrences
around the request-affinity logic) with this constant to make the policy
explicit and consistent with REQUEST_AFFINITY_KEY_MAX_CHARS.

Comment on lines +584 to +589
fn looks_like_json_payload(text: &str) -> bool {
matches!(
serde_json::from_str::<Value>(text),
Ok(Value::Object(_) | Value::Array(_))
)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

JSON parse on every candidate could be costly for large tool-result payloads.

looks_like_json_payload parses the entire string to detect object/array structure. For multi-KB tool results this adds overhead on every affinity check. A prefix heuristic (check if trimmed text starts with { or [ before parsing) would short-circuit obvious non-JSON cases.

♻️ Suggested optimization
 fn looks_like_json_payload(text: &str) -> bool {
+    let first = text.trim_start().chars().next();
+    if !matches!(first, Some('{') | Some('[')) {
+        return false;
+    }
     matches!(
         serde_json::from_str::<Value>(text),
         Ok(Value::Object(_) | Value::Array(_))
     )
 }
🤖 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 `@crates/cli/src/alignment/mod.rs` around lines 584 - 589, The function
looks_like_json_payload currently attempts to parse the entire input with
serde_json on every call; change it to first trim leading whitespace and
short-circuit non-JSON by returning false unless the first non-whitespace char
is '{' or '[' (indicating object/array), and only then call
serde_json::from_str::<Value> to confirm Ok(Value::Object(_) | Value::Array(_));
update the function name and logic where needed to reference
looks_like_json_payload so callers get the optimized behavior.

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

Labels

Bug issue describes bug; PR fixes bug lang:rust PR changes/introduces Rust code size:XXL PR is very large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant