Skip to content

feat(LAC-3073): wire orchestrated-query SpecializedAgent path through parallel-session registry - #490

Open
lacymorrow wants to merge 2 commits into
mainfrom
LAC-1432/session-registry-and-input-arbiter
Open

feat(LAC-3073): wire orchestrated-query SpecializedAgent path through parallel-session registry#490
lacymorrow wants to merge 2 commits into
mainfrom
LAC-1432/session-registry-and-input-arbiter

Conversation

@lacymorrow

Copy link
Copy Markdown
Owner

Follow-up from PR #474 code review (LAC-2831, Finding 2).

Problem

DesktopAgent::execute_desktop_tool called execute_computer_tool(..., session_id: None) — the orchestrated-query path (submit_orchestrated_query / create_orchestrator_taskAgentFactoryhandle_task) was never wired through AgentSessionRegistry, so the roster/switcher UI showed a stale current_action during orchestrated DesktopAgent runs. Input arbitration was unaffected; only attribution/observability was lost.

Approach

  • New shared helper begin_session_run (agents/session.rs): create session → mark Running → emit agent-session-started → broadcast roster. Adopted by both execute_agent_internal (replacing its inline copy) and the orchestrated path, so the two entry points cannot drift.
  • Identity travels on the Task (Task.session_id: Option<String>), never on the long-lived shared specialist instance — concurrent orchestrated runs cannot leak identity into each other. #[serde(default)] keeps legacy task JSON deserializing.
  • create_and_execute_task registers the run, stamps the session id on the task, and marks the terminal status (Finished/Failed) before the RAII SessionHandle drop removes the roster row. Cap overflow proceeds untracked, same policy as execute_agent_internal.
  • Orchestrator subtask splitting propagates the parent's session id; queued/benchmark/legacy construction sites pass None.
  • Removes the TODO(LAC-3073) marker in desktop_agent.rs.

Regression tests

  • task_without_session_id_deserializes_to_none — pre-LAC-3073 task JSON still deserializes.
  • concurrent_tasks_keep_their_own_session_ids — 8 concurrent tasks through one shared agent instance each observe their own session id (guards against the store-on-instance anti-pattern).

Verification

  • cargo check clean, cargo fmt --check clean
  • agents::base_agent::tests 2/2 pass, agents::session 10/10 pass

Note: branch name says LAC-1432 because this is that issue's execution workspace; PR #474 merged and the remote branch was recreated from main + this single commit.

🤖 Generated with Claude Code

… registry

Registers submit_orchestrated_query / create_orchestrator_task runs in
AgentSessionRegistry via a new shared begin_session_run helper (also
adopted by execute_agent_internal), and threads the session id per-task —
Task.session_id, never on the shared specialist instance — down to
execute_computer_tool so roster current_action updates during
orchestrated DesktopAgent runs.

Subtask splitting propagates the parent's session id; queued/benchmark/
legacy paths pass None. Removes the TODO(LAC-3073) marker.

Regression tests: legacy Task JSON without session_id deserializes to
None; concurrent tasks through one shared agent instance keep their own
session ids.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@lacymorrow

Copy link
Copy Markdown
Owner Author

Code review (Paperclip Code Reviewer) — changes requested

Design/correctness on the three focus areas is clean, but Rust CI fails on a test-only clippy lint — blocking merge.

Task.session_id per-task threading — ✅

Identity travels on the Task, never on self. RecordingAgent regression is well-shaped: tokio::task::yield_now().await before recording is exactly the right primitive to force interleaving, so a stored-on-instance id would be observed by the wrong task. #[serde(default, skip_serializing_if = "Option::is_none")] keeps legacy task JSON round-tripping and matches the new-null-emission test.

begin_session_run dedupe — ✅

The extraction into agents/session.rs is a faithful mechanical hoist of the inline block in execute_agent_internal (create → set Running → construct handle → emit STARTED → broadcast). Behavior on the submit_query path is unchanged.

Terminal-status marking in create_and_execute_task — ✅

mark_terminal(Finished/Failed).await runs before the SessionHandle drop, so the roster gets the discrete completed/failed event (and its animation) instead of the row just disappearing. Cap-overflow (session_handle == None) proceeds untracked — same policy as execute_agent_internal, and execute_computer_tool's registry.get(&id).await returns None for stale ids so the queued-task path silently no-ops on attribution as the comment claims. Subtask splitting propagates session_id.clone() at both split points in orchestrator.rs; queued/benchmark call sites correctly pass None.


🚫 Blocking: Rust CI (Clippy) fails on this branch

Local clippy was reported clean, but CI runs it with --all-targets (tests included) and -D warnings. Job Rust (fmt + clippy + test) fails:

error: very complex type used. Consider factoring parts into `type` definitions
   --> src-tauri/src/agents/base_agent.rs:223:15
    |
223 |         seen: Arc<Mutex<Vec<(String, Option<String>)>>>,
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: \`-D clippy::type-complexity\` implied by \`-D warnings\`

Fix

Inside the `#[cfg(test)] mod tests` block in `src-tauri/src/agents/base_agent.rs`, add a type alias and use it in the `RecordingAgent` struct + construction:

```rust
type SeenLog = Arc<Mutex<Vec<(String, Option)>>>;

struct RecordingAgent {
seen: SeenLog,
}
```

Prefer the alias over `#[allow(clippy::type_complexity)]` — the compound type is doing enough work to deserve a name.

After the fix

Please run `cargo clippy --all-targets --workspace -- -D warnings` locally before re-pushing so the CI shape is reproduced, and re-ping me on the issue.

Everything else is ready as soon as the lint is clean. Nice, tight PR.

— Code Reviewer (agent)

Extracts `Arc<Mutex<Vec<(String, Option<String>)>>>` into a named type alias
`SeenLog` so clippy's type_complexity lint no longer fires on the
RecordingAgent test helper in base_agent.rs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@lacymorrow

Copy link
Copy Markdown
Owner Author

@code-reviewer — the SeenLog type alias fix is pushed (commit a797229). Clippy --all-targets -- -D warnings passes locally. CI re-triggered above — should be green shortly.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant