Skip to content

feat(agents) 1/5: a session has an origin and a name, and agents are declared - #181

Merged
senamakel merged 3 commits into
mainfrom
split/1-agent-model
Aug 5, 2026
Merged

feat(agents) 1/5: a session has an origin and a name, and agents are declared#181
senamakel merged 3 commits into
mainfrom
split/1-agent-model

Conversation

@sanil-23

@sanil-23 sanil-23 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Stack 1/5 — base main. Depends on: nothing.

# PR Branch Base Layer
1 #181 split/1-agent-model main session origin/name + agent declarations (model only)
2 #182 split/2-tree-ui split/1-agent-model Hosts and Agents tabs become one Host → Agent → Session tree
3 #183 split/3-vocabulary split/2-tree-ui rename: "harness" is a type, not an entity
4 #184 split/4-wire split/3-vocabulary hub advert: hosts[], per-agent hostId/maxSessions, result sessionId
5 #185 split/5-control split/4-wire dispatch candidacy, hold/hand-back, host-wide cap removed

Each PR's diff shows only its own layer. split/5-control's tree is byte-identical to
bcc61e63, the merge commit on #180.


This is the first of five stacked PRs that replace #180 (feat/agent-rail, 230 files
across 11 commits). CodeRabbit skips anything over 150 files and no human can review a
diff that size, so the branch was linearised onto main and cut at commit boundaries
into five layers that each build and test on their own. #180 stays open, unchanged,
as the reference for the whole change — please do not close it.
The five branches
together are byte-identical to its merge commit bcc61e63.

What this layer does

The data model, with no UI and no wire-shape change.

feat(sessions): give a session an origin and a name

A task is an agent session; what separates the orchestrator's sessions from the ones a
person spins up is provenance, not type. So a session gains two identity fields:

  • origin: SessionOriginOrchestrator (auto-created by a dispatch) or User (spun
    up from the UI). Fixed at creation, for life.
  • name: Option<String> — what the person who started it called it; None for a
    dispatched session, which the UI labels from its task.

Origin is deliberately not ownership. HarnessControl says who may start a turn
right now and moves on every take and hand-back; origin says who started the session and
never moves. The layout says so too — origin sits in the pty handle's immutable
SessionMeta, control stays an atomic bit. user_spawned was the precursor of origin
and is replaced by it rather than duplicated, so the two can never disagree.

feat(fleet): declare agents and build the local roster from them

An agent is harness × workspace on a host, and until now nothing wrote one down.
AgentDeclaration becomes the source of the roster: persisted under
[fleet].agentDeclarations, spec_for becomes specs_for (one WorkerSpec per
declared agent rather than one per daemon), HubWorker gains host_id and
strategy-derived max_sessions, and roles finally have somewhere to persist to.

What a reviewer should look at closely

  • origin vs control. The invariant is that a dispatched session an operator holds
    is origin: Orchestrator, control: User — both true at once, and only control gates
    dispatch. Anywhere origin is read to decide behaviour rather than labelling would
    be a bug.
  • The migration seed in specs_for. A host that has declared nothing seeds one agent
    per detected CLI, and the default keeps the host's own id, so an install predating
    declarations advertises exactly the entry it had. This is the path that stops an
    existing user's roster going empty on upgrade.
  • strategy derives concurrency (checkout ⇒ 1 session, serial). worktree parses
    but is not selectable — nothing provisions a worktree yet.
  • No wire change here. The advert is unchanged: the path still rides
    metadata.workspace, and neither hostId nor maxSessions is emitted. That lands in
    stack 4/5.

Validation

cargo fmt --all -- --check
cargo clippy --locked --all-targets -- -D warnings
cargo check --locked --all-targets
cargo build --locked --all-targets
cargo test --locked --no-fail-fast

Result on this branch: fmt, clippy -D warnings, check, and build all clean.
test reports 3810 passed, 5 failed, 14 ignored on macOS. All five failures are
pre-existing on main and unrelated to this stack:

  • daemon::providers::acp::tests::execution::a_new_acp_session_is_reported_before_the_task_completes
  • daemon::providers::tests::direct_runs_report_the_session_before_workspace_context
    (both are idle-watchdog timing tests; they pass in isolation)
  • worker::pty::tests::session::a_launch_root_preserves_trailing_whitespace
  • worker::pty::tests::session::a_session_snapshots_head_before_the_harness_can_commit
  • worker::pty::tests::session::an_unborn_repository_records_its_root_without_a_launch_commit
    (the last three are the macOS /var vs /private/var symlink)

Coverage was deliberately not run locally; CI owns that gate.

Summary by CodeRabbit

  • New Features
    • Added support for locally declared agents with configurable hosts, harnesses, workspaces, roles, names, checkout strategies, and session limits.
    • Local hosts can advertise and run multiple agents, including automatic fallback roster creation when declarations are absent.
    • Added session names and clear indicators distinguishing user-created and orchestrator-created sessions.
    • Improved agent selection when multiple agents share a host address.
  • Documentation
    • Added configuration guidance for declaring local agents and default roster behavior.
  • Bug Fixes
    • Workspace information is presented consistently as a path in worker listings.
    • Added validation to reject agents assigned to conflicting workspaces.

sanil-23 and others added 2 commits August 5, 2026 10:50
A task is an agent session. What separates the orchestrator's sessions
from the ones a person spins up is not their type but their *provenance*,
so sessions gain the two identity fields that make one row taxonomy
possible (topology plan B3, spec §2.2):

- `origin: SessionOrigin` — `Orchestrator` (auto-created by a dispatch,
  §4.1) or `User` (spun up from the UI). Fixed at creation, for life.
- `name: Option<String>` — what the person who started it called it;
  `None` for a dispatched session, which the UI labels from its task.

Origin is deliberately *not* ownership. Control (`HarnessControl`) says
who may start a turn right now and moves on every take and hand-back;
origin says who started the session and never moves. The layout says so
too: origin sits in the pty handle's immutable `SessionMeta` beside the
launch anchor, while control stays an atomic bit. A dispatched session an
operator holds is `origin: orchestrator, control: user` — both true at
once, and only control gates dispatch.

`user_spawned` was the precursor of this field and is replaced by it
rather than duplicated, so the two can never disagree.

Set on both creation paths: the executor's `session_for` stamps
`Orchestrator`, the operator's `open_unmanaged` stamps `User`. The SDK
manager takes it from `OpenSession`, and auto-creation derives it from
the turn's own provenance (`TurnOrigin::session_origin`). The binding
registry carries a `SessionIdentity` beside its `WorkspaceContext` and
keeps it across a rebind, so a resumed conversation does not come back
unnamed.

No wire change; `open_unmanaged_named` is the seam the picker's future
name prompt lands on.

Co-Authored-By: Claude <noreply@anthropic.com>
An agent is `harness × workspace` on a host, and until now nothing wrote
one down: `[fleet]` held the whole `Host → Harness → Workspace → Agent`
chain and was display-only, while the roster synthesized one entry per
machine. A laptop running claude *and* codex advertised a single worker
whose second CLI survived only as prose in its description — and prose is
not something a dispatch can target.

Declarations become the source of the roster:

- `AgentDeclaration` (`runtime/fleet/declaration.rs`): `agentId`, `hostId`,
  `harness`, `workspace {path, type}`, optional `name`, `roles`, and a
  `strategy` that *derives* concurrency (`checkout` ⇒ 1 session, serial).
  `worktree` exists as a variant so a config naming it still parses, but
  it is not selectable — nothing provisions a worktree yet.
- Persistence in `[fleet].agentDeclarations`, written through the same
  `persist_setting` path the rest of the section already uses, with
  create/update/remove and the queries the UI and the roster read back
  (`config/agent_declarations.rs`).
- `spec_for` becomes `specs_for`: one `WorkerSpec` per declared agent
  instead of one per daemon. A host that has declared nothing seeds one
  agent per detected CLI, and the default one keeps the host's own id — so
  an install that predates declarations advertises exactly the entry it
  had, and nobody's roster goes empty.
- `HubWorker` gains `host_id` and strategy-derived `max_sessions`, and its
  workspace becomes `{path, type}`; `control`/`handoff` are untouched, and
  the advert is unchanged — the path still rides `metadata.workspace`, and
  neither `hostId` nor `maxSessions` is emitted yet.
- Roles finally have a source: they are declared on the agent and reach
  `metadata.roles` through the spec, where the mapping used to hard-code an
  empty list. The remembered roster now persists them too.
- A dispatch's task is filed under the agent it named rather than the
  first entry at that address, which several agents now share.

Model and data only: no wire-shape change, no UI.

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

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

sanil-23 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dd3e461d-6624-426e-93b4-c52e21dd9c03

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds persisted agent declarations, structured workspace and roster metadata, multi-agent local host advertising, session provenance tracking, optional session names, and SDK and TUI tests.

Changes

Agent declarations and configuration

Layer / File(s) Summary
Declaration and workspace model
src/sdk/src/runtime/fleet/*
Adds workspace references, strategies, agent declarations, migration seeding, collision-free IDs, and public exports.
Configuration persistence and CRUD
config.example.toml, src/sdk/src/config/*
Adds fleet.agentDeclarations, loading, querying, mutation, persistence, and round-trip tests.

Multi-agent roster integration

Layer / File(s) Summary
Worker and roster contracts
src/sdk/src/hub/boot/*, src/sdk/src/hub/roster/*
Worker specifications and roster entries now carry structured workspaces, host IDs, roles, and session capacity.
Local host expansion
src/tui/src/local_host/*, src/tui/src/app_loop.rs, src/tui/src/hub_relay/*
Local hosts advertise one worker specification per declaration and seed declarations when none exist.
Dispatch and validation
src/sdk/src/hub/socket/*, src/sdk/src/hub/tests/*, src/tui/src/local_host/tests/*
Dispatch lane selection handles multiple agents sharing an address. Tests cover routing, naming, roles, workspaces, and capacity.

Session provenance and naming

Layer / File(s) Summary
Session contracts and registry
src/sdk/src/sessions/*
Adds SessionOrigin and SessionIdentity, records optional names, and preserves identity across resumed turns and rebinding.
PTY and TUI integration
src/tui/src/worker/pty/*, src/tui/src/ui/harness_pane/*, src/tui/src/worker/executor/*
Replaces user_spawned with immutable origin metadata and supports optional session names.
Validation and fixtures
src/sdk/src/sessions/tests/*, src/tui/src/worker/*tests*, src/tui/tests/*
Tests origin derivation, identity persistence, naming, handoffs, and user/orchestrator behavior.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: senamakel

Poem

A rabbit maps each agent’s place,
With names and workspaces in steady space.
Sessions keep their origin clear,
Through every handoff, far and near.
Config and rosters neatly grow—
Hop, hop, declarations flow!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two main changes: session identity fields and persisted agent declarations.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/tui/src/event_loop/cmd_dispatch/mod.rs (1)

216-242: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Register every declared agent for a dynamically started host.

spawn returns all local WorkerSpec values, but this handler takes only the first one. All sibling agents remain absent from the live roster until restart. The address-based WorkerOp::Add also cannot preserve each agent ID, role, workspace, or capacity.

Add an agent-keyed runtime roster operation, then register every returned specification through it.

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

In `@src/tui/src/event_loop/cmd_dispatch/mod.rs` around lines 216 - 242, Update
the dynamic host-start handling in the spawn branch to process every returned
WorkerSpec instead of selecting only the first. Add and use an agent-keyed
runtime roster operation that preserves each specification’s agent ID, role,
workspace, and capacity, replacing the address-based WorkerOp::Add path so every
declared agent is immediately registered in the live roster.
🧹 Nitpick comments (2)
src/tui/src/ui/harness_pane/tests/session.rs (1)

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

Document the visible test helper.

Add a /// comment to harnesses. Line 55 changes it to a pub(super) function. The Rust guidelines require documentation for every public function.

Proposed fix
+/// Build local harnesses backed by `sessions` for harness-pane tests.
 pub(super) fn harnesses(sessions: PtyManager) -> LocalHarnesses {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tui/src/ui/harness_pane/tests/session.rs` at line 55, Add a concise Rust
doc comment (`///`) immediately above the pub(super) test helper harnesses,
describing its purpose and documenting the newly visible function without
changing its implementation.

Source: Coding guidelines

src/sdk/src/hub/tests/roster.rs (1)

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

Split src/sdk/src/hub/tests/roster.rs. The file contains 537 non-comment, non-blank lines, exceeding the 500-line limit. Move related tests into separate modules.

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

In `@src/sdk/src/hub/tests/roster.rs` around lines 607 - 694, Split the oversized
roster test file into focused modules, moving the related declaration/advert,
capacity-default, and lane-resolution tests around worker_from_spec and lane_id
into an appropriate separate test module. Preserve all test behavior, shared
helpers, imports, and module visibility while keeping
src/sdk/src/hub/tests/roster.rs under the 500 non-comment, non-blank line limit.

Source: Coding guidelines

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

Inline comments:
In `@src/sdk/src/config/agent_declarations_tests.rs`:
- Around line 20-35: Add a JSON-file round-trip test alongside
a_declared_agent_round_trips_through_the_config_file, using a .json scratch path
and the same declare_agent/load_agent_declarations flow. Assert the declared
agent is written and reloaded with matching fields, including max_sessions(), to
cover the non-TOML JSON parsing branch.

In `@src/sdk/src/control_socket/server/hub_ops.rs`:
- Around line 120-122: Normalize workspace values in both projections by
replacing direct WorkspaceRef.path access with
worker.workspace_path().map(str::to_owned). Apply this in
src/sdk/src/control_socket/server/hub_ops.rs lines 120-122 for
FleetWorker.workspace and src/sdk/src/runtime/openhuman/worker_ops.rs lines
36-38 for WorkerInfo.workspace, preserving omission of blank or
whitespace-padded paths.

In `@src/sdk/src/runtime/fleet/declaration.rs`:
- Around line 118-122: Update max_sessions in
src/sdk/src/runtime/fleet/declaration.rs lines 118-122 so both Checkout and the
unsupported Worktree strategy return a capacity of one; retain
WORKTREE_MAX_SESSIONS only if it is no longer used elsewhere. Update the
assertion in src/sdk/src/runtime/fleet/declaration_tests.rs lines 7-14 to expect
one session for Worktree instead of parallel capacity.

In `@src/sdk/src/sessions/manager/turns.rs`:
- Around line 189-194: Update the session recreation flow around ensure_session
to resolve the registry plan first and, when resuming an existing binding, reuse
its persisted identity rather than deriving it from
request.origin.session_origin(). Keep TurnOrigin-based identity only for
sessions without a binding, preserving the resumed user session’s origin and
name after close; add a regression test covering close → frame turn → resumed
user origin and name.

In `@src/sdk/src/sessions/registry/types.rs`:
- Around line 48-52: Prevent bound session origins from being changed through
SessionIdentity updates: either make SessionIdentity::origin non-public or
update SessionRegistry::record_identity to retain the previously stored origin.
Add or use a name-only update API for changing the mutable display name without
replacing origin.

In `@src/tui/src/local_host/mod.rs`:
- Around line 353-370: Update the local-host dispatch flow so each selected
agent runs with its declared workspace instead of the host-wide
options.workspace: propagate the selected agent identity from shared-address
dispatch into executor creation and construct or select a PtySessionExecutor
using that declaration’s workspace. Alternatively, reject declarations with
differing workspaces before dispatch, but preserve the advertised workspace
contract. Add a dispatch test covering two declarations on one host with
different workspaces.
- Around line 343-351: Update the label-generation logic in the declaration
mapping around the single/multiple declaration handling so sibling declarations
sharing the same harness receive distinct labels by including their agent_id or
workspace; retain existing labels when harnesses are unique. Add a regression
test covering multiple local declarations with the same harness and assert that
their generated labels differ.

---

Outside diff comments:
In `@src/tui/src/event_loop/cmd_dispatch/mod.rs`:
- Around line 216-242: Update the dynamic host-start handling in the spawn
branch to process every returned WorkerSpec instead of selecting only the first.
Add and use an agent-keyed runtime roster operation that preserves each
specification’s agent ID, role, workspace, and capacity, replacing the
address-based WorkerOp::Add path so every declared agent is immediately
registered in the live roster.

---

Nitpick comments:
In `@src/sdk/src/hub/tests/roster.rs`:
- Around line 607-694: Split the oversized roster test file into focused
modules, moving the related declaration/advert, capacity-default, and
lane-resolution tests around worker_from_spec and lane_id into an appropriate
separate test module. Preserve all test behavior, shared helpers, imports, and
module visibility while keeping src/sdk/src/hub/tests/roster.rs under the 500
non-comment, non-blank line limit.

In `@src/tui/src/ui/harness_pane/tests/session.rs`:
- Line 55: Add a concise Rust doc comment (`///`) immediately above the
pub(super) test helper harnesses, describing its purpose and documenting the
newly visible function without changing its implementation.
🪄 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: Pro

Run ID: bcb719e2-6b7a-42ed-a3e3-e5920678ffa6

📥 Commits

Reviewing files that changed from the base of the PR and between 48737e5 and 59faf21.

📒 Files selected for processing (75)
  • config.example.toml
  • src/sdk/src/config/README.md
  • src/sdk/src/config/agent_declarations.rs
  • src/sdk/src/config/agent_declarations_tests.rs
  • src/sdk/src/config/mod.rs
  • src/sdk/src/config/persist.rs
  • src/sdk/src/config/types/fleet.rs
  • src/sdk/src/config/types/mod.rs
  • src/sdk/src/control_socket/server/hub_ops.rs
  • src/sdk/src/hub/boot/mod.rs
  • src/sdk/src/hub/boot/types.rs
  • src/sdk/src/hub/handle/handoff.rs
  • src/sdk/src/hub/roster/mod.rs
  • src/sdk/src/hub/roster/types.rs
  • src/sdk/src/hub/socket/task_run.rs
  • src/sdk/src/hub/tests/handoff_advert.rs
  • src/sdk/src/hub/tests/roster.rs
  • src/sdk/src/runtime/fleet/README.md
  • src/sdk/src/runtime/fleet/declaration.rs
  • src/sdk/src/runtime/fleet/declaration_tests.rs
  • src/sdk/src/runtime/fleet/mod.rs
  • src/sdk/src/runtime/mod.rs
  • src/sdk/src/runtime/openhuman/tests.rs
  • src/sdk/src/runtime/openhuman/worker_ops.rs
  • src/sdk/src/sessions/manager/mod.rs
  • src/sdk/src/sessions/manager/turns.rs
  • src/sdk/src/sessions/manager/types.rs
  • src/sdk/src/sessions/mod.rs
  • src/sdk/src/sessions/ops/mod.rs
  • src/sdk/src/sessions/registry/behavior.rs
  • src/sdk/src/sessions/registry/mod.rs
  • src/sdk/src/sessions/registry/types.rs
  • src/sdk/src/sessions/tests/manager_tests/lifecycle.rs
  • src/sdk/src/sessions/tests/manager_tests/mod.rs
  • src/sdk/src/sessions/tests/ops_tests.rs
  • src/sdk/src/sessions/tests/registry_tests.rs
  • src/sdk/src/sessions/types.rs
  • src/tui/examples/pty_load.rs
  • src/tui/src/app_loop.rs
  • src/tui/src/event_loop/cmd_dispatch/mod.rs
  • src/tui/src/hub_relay/mod.rs
  • src/tui/src/local_host/mod.rs
  • src/tui/src/local_host/tests/declarations.rs
  • src/tui/src/local_host/tests/extras.rs
  • src/tui/src/local_host/tests/lifecycle.rs
  • src/tui/src/local_host/tests/mod.rs
  • src/tui/src/local_host/types.rs
  • src/tui/src/ui/app/changes/baseline_tests.rs
  • src/tui/src/ui/app/rail.rs
  • src/tui/src/ui/app/render/agents/rail/tests.rs
  • src/tui/src/ui/app/render/settings/status_line.rs
  • src/tui/src/ui/harness_pane/spawn.rs
  • src/tui/src/ui/harness_pane/tests/mod.rs
  • src/tui/src/ui/harness_pane/tests/origin.rs
  • src/tui/src/ui/harness_pane/tests/session.rs
  • src/tui/src/worker/app/tests/helpers/mod.rs
  • src/tui/src/worker/executor/run.rs
  • src/tui/src/worker/executor_tests/basic.rs
  • src/tui/src/worker/executor_tests/live.rs
  • src/tui/src/worker/executor_tests/sessions.rs
  • src/tui/src/worker/pty/handle/control.rs
  • src/tui/src/worker/pty/handle/lifecycle.rs
  • src/tui/src/worker/pty/handle/state.rs
  • src/tui/src/worker/pty/handle/types.rs
  • src/tui/src/worker/pty/manager/open.rs
  • src/tui/src/worker/pty/manager/session.rs
  • src/tui/src/worker/pty/mod.rs
  • src/tui/src/worker/pty/tests/control.rs
  • src/tui/src/worker/pty/tests/mod.rs
  • src/tui/src/worker/pty/types.rs
  • src/tui/tests/e2e_local_harness_pane.rs
  • src/tui/tests/e2e_screen_stream.rs
  • src/tui/tests/feature_harness_control.rs
  • src/tui/tests/feature_harness_handoff.rs
  • src/tui/tests/feature_paste/attached.rs

Comment thread src/sdk/src/config/agent_declarations_tests.rs
Comment thread src/sdk/src/control_socket/server/hub_ops.rs Outdated
Comment thread src/sdk/src/runtime/fleet/declaration.rs
Comment thread src/sdk/src/sessions/manager/turns.rs Outdated
Comment thread src/sdk/src/sessions/registry/types.rs
Comment thread src/tui/src/local_host/mod.rs
Comment thread src/tui/src/local_host/mod.rs Outdated
Comment on lines +353 to +370
let workspace = declaration.workspace.clone();
WorkerSpec {
id: declaration.agent_id.clone(),
host_id: host_id.to_string(),
address: host_id.to_string(),
name: label,
description: format!(
"{} on this machine · {}",
declaration.harness, workspace.path
),
harness: declaration.harness.clone(),
// The one placement this process actually knows: the agent works
// in this directory. Declaring it is what gives the orchestrator
// a placed agent rather than a bare one it treats as having
// nowhere to work.
workspace: Some(workspace),
roles: declaration.roles.clone(),
max_sessions: declaration.max_sessions(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Run each declared agent in its declared workspace.

specs_for advertises declaration.workspace, but start_at creates one PtySessionExecutor from options.workspace for the entire host. The shared-address dispatch path selects a host address, not a declaration workspace. Agents declared for different repositories will therefore run in the host workspace while advertising another workspace.

Route the selected agent identity into executor selection, or reject declarations whose workspace differs from the host workspace. Add a dispatch test with two declarations on one host and different workspaces.

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

In `@src/tui/src/local_host/mod.rs` around lines 353 - 370, Update the local-host
dispatch flow so each selected agent runs with its declared workspace instead of
the host-wide options.workspace: propagate the selected agent identity from
shared-address dispatch into executor creation and construct or select a
PtySessionExecutor using that declaration’s workspace. Alternatively, reject
declarations with differing workspaces before dispatch, but preserve the
advertised workspace contract. Add a dispatch test covering two declarations on
one host with different workspaces.

…tity

Review fixes on the agent-declaration model.

An agent may no longer declare a workspace its host does not run in. A host
binds one address and serves every agent on it from the single executor it
started in its own directory — a task frame carries no agentId to select on —
so two agents declared for two repositories both ran in the host's while
advertising two. Refused at start-up, by name, pointing at the [[hosts]] entry
that does give a second directory; the advert now always names the directory
the executor actually launches in. Per-agent workspaces need the selected
agent's id on the wire, which is a protocol change and not this branch's.

Siblings that share a harness are labelled by their agent id: "this device ·
claude" twice named neither.

Worktree capacity drops to one session. Nothing provisions a worktree yet, so
advertising four scheduled four concurrent sessions into one checkout — the
collision the strategy exists to prevent, reached by declaring the fix.

A session's origin is now immutable in fact, not only in prose: the field is
private, record_identity establishes it once and preserves it afterwards, and
rename() is the name-only path. A resumed binding's identity also wins over the
turn that reopened it, so a closed user session recreated by a task frame keeps
its owner and its name instead of coming back as an unnamed orchestrator one.

Both hub worker projections read the workspace through workspace_path(), so a
blank declaration is absent everywhere it is absent in the advert. JSON config
persistence of declarations is now covered.

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

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

sanil-23 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@sanil-23

sanil-23 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed.

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

@sanil-23

sanil-23 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed.

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

@senamakel
senamakel merged commit bd7b237 into main Aug 5, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants