Skip to content

CodeRabbit shadow review for PR #1#6

Closed
TraderSamwise wants to merge 45 commits into
coderabbit/pr1-basefrom
coderabbit/pr1-head
Closed

CodeRabbit shadow review for PR #1#6
TraderSamwise wants to merge 45 commits into
coderabbit/pr1-basefrom
coderabbit/pr1-head

Conversation

@TraderSamwise

@TraderSamwise TraderSamwise commented May 21, 2026

Copy link
Copy Markdown
Owner

Purpose

Temporary shadow PR to let CodeRabbit review the already-merged PR #1 diff.

Original PR: #1
Original base commit: 2f2ea84
Original head commit: 0b86517

Do not merge this PR. It exists only for review tooling.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added teammate agents system enabling agents to create and manage "teammate" sub-agents with creation, lifecycle management (stop/resume/kill/resurrect), and delegation capabilities.
    • New aimux debug-state <target> command for troubleshooting session/service/worktree state.
    • Teammate picker UI overlay for navigating between parent agent and its teammates.
    • Team navigation control in statusline and dashboard interface.
  • Documentation

    • Extended README with teammate agents HTTP API endpoints and usage documentation.
  • Improvements

    • Enhanced pending action state visualization across dashboard and statusline.
    • Improved offline session recovery with backend session ID capture.
    • Better teammate-aware worktree and service lifecycle handling.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds first-class teammate agents across API/UI/runtime with team-aware navigation and rendering, centralizes typed pending actions for sessions/services/worktrees, extends tmux with a “team” command, tightens daemon project-service lifecycle, and updates dashboards, persistence, tests, and docs accordingly.

Changes

Teammates + Pending + Tmux

Layer / File(s) Summary
Teammate feature, pending types/flows, tmux team navigation, lifecycle refactors
README.md, src/metadata-server.*, src/multiplexer/**/*, src/pending-actions.ts, src/team.*, src/tmux/**/*, desktop-ui/*, src/statusline-*, src/session-*, src/daemon.*, src/paths.ts, tests/docs/CLI
Implements teammate agents APIs and UI, introduces typed pending-action targets, adds tmux “team” navigation, updates dashboard/multiplexer/state/runtime/persistence, improves daemon ensure/replace, and expands tests, docs, and a debug-state CLI.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Desktop UI
  participant Server as MetadataServer
  participant Model as DashboardModel
  participant Ops as SessionActions
  participant Tmux as TmuxRuntimeManager
  Client->>Server: POST /agents/teammates/create (parentSessionId,…)
  Server->>Ops: createTeammateAgent(...)
  Ops-->>Server: {sessionId, teamId, reused?}
  Server-->>Client: Teammate created/reused
  Client->>Model: withMetadataSessionPending(starting)
  Model-->>Tmux: statusline refresh (force)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~180 minutes

Poem

I gathered my crew in a tidy warren,
three teammates max, no extra-burrow foreign.
We hop between panes with a tap on “e”,
pending dots blink—soon set free.
Logs hum, tmux sings, dashboards gleam—
ship it, squad—teamwork supreme! 🐇✨

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch coderabbit/pr1-head

@TraderSamwise
TraderSamwise marked this pull request as draft May 21, 2026 15:10
@TraderSamwise

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

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

Caution

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

⚠️ Outside diff range comments (1)
src/dashboard/pending-actions.ts (1)

315-337: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent stale settle callbacks from clearing newer pending actions.

settleCreatePending always calls clearEntry(target, itemId). If a newer pending action for the same target/id is set before this async flow completes, the newer action is removed accidentally.

Suggested fix
 export function settleCreatePending(
   target: PendingActionTarget,
   itemId: string,
   onSettled: () => void,
-  opts?: { isSettled?: () => boolean | Promise<boolean>; timeoutMs?: number },
+  opts?: { isSettled?: () => boolean | Promise<boolean>; timeoutMs?: number; expectedToken?: number },
 ): void {
@@
-      this.clearEntry(target, itemId);
+      if (opts?.expectedToken !== undefined) {
+        this.clearEntryIfToken(target, itemId, opts.expectedToken);
+      } else {
+        this.clearEntry(target, itemId);
+      }
       onSettled();
🤖 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/dashboard/pending-actions.ts` around lines 315 - 337, settleCreatePending
currently unconditionally calls clearEntry(target, itemId) after an async wait
which can remove a newer pending action; fix by recording a unique marker when
this settle flow starts (e.g., capture startedAt or a generated token) and, just
before calling clearEntry, verify the current pending entry for target/itemId
still matches that marker (compare the entry's createdAt or token stored when
the pending action was enqueued); only call clearEntry(target, itemId) and
invoke onSettled() if the markers match, otherwise skip clearing so newer
pending actions are not removed.
🤖 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 `@scripts/tmux-control.sh`:
- Around line 446-473: The selection can pick a direct teammate with no
tmuxWindowId even though another direct teammate is live; update the logic that
computes direct/target to filter for live windows before choosing the first
entry: after building and sorting direct (or immediately after constructing
direct), filter it (e.g., into a new list like direct_live) to only include
sessions where session.get("tmuxWindowId") is truthy (and/or
session.get("status") == "live" if you prefer status-based filtering), then use
that filtered list when checking emptiness, selecting target, and logging
(adjust logs that reference direct[0] to use the first item from the filtered
list); keep teammate_order, parse_time and sorting unchanged but apply them to
the filtered list so you never pick a teammate without a tmuxWindowId for
target.

In `@src/dashboard/ui-state-store.ts`:
- Around line 40-52: The loadSharedState method can leave stale orderState when
JSON read/parse fails; before the try block reset this.orderState to an empty
mapping (e.g., agentOrderByWorktreeKey and serviceOrderByWorktreeKey as empty
maps/objects) so that if readFileSync/JSON.parse in loadSharedState throws,
orderState is not left from a previous load; update loadSharedState to set
this.orderState = { agentOrderByWorktreeKey: sanitizeOrderMap({}),
serviceOrderByWorktreeKey: sanitizeOrderMap({}) } (or equivalent empty
structures) before the try, referencing loadSharedState, orderState, and
sanitizeOrderMap.

In `@src/debug-state.ts`:
- Around line 417-430: filterNotifications currently filters notifications but
never records hits in the target resolution, so notifications-only matches stay
missing; update filterNotifications(source, target) to call addMatch with the
target (and appropriate context/path or identifier) whenever the computed
notifications array is non-empty (i.e., when any entry matched), then return the
same SourceResult ({ status: "found", path: source.path, value: { notifications
} }) so targetResolution sees the match; reference the functions/values
filterNotifications, addMatch, source, target, and the returned notifications to
locate and implement the change.

In `@src/metadata-server.ts`:
- Around line 2021-2041: The POST /agents/teammates/create handler forwards
requests to this.options.lifecycle.createTeammateAgent without validating or
authorizing body.parentSessionId; update the handler to extract parentSessionId
from the body, run the same parent-session validation/authorization used by
other teammate endpoints (reuse the existing helper or function that checks
session existence, ownership, and prevents nested-team parents — e.g., the
validate/authorize function used elsewhere for teammate routes), and if invalid
return the appropriate 400/403 error before calling
this.options.lifecycle.createTeammateAgent; only call this.options.onChange()
and send 200 on successful creation.

In `@src/multiplexer/dashboard-control.ts`:
- Around line 626-630: The code in postToProjectService currently retries on
attempt === 0 regardless of status, which can resend non-retryable failures
(e.g., 409/500) and cause duplicate non-idempotent writes; change the retry
condition so that it only retries when isProjectServiceRetryableStatus(status)
is true (remove the attempt === 0 clause), keeping the existing retry flow that
calls ensureDashboardControlPlane(host) and sleepProjectServiceRetry(attempt)
only for retryable statuses; update any related comments and ensure lastError
still captures the failure for non-retryable responses so the function returns
or throws without retrying.

In `@src/multiplexer/dashboard-model.ts`:
- Around line 318-321: The catch blocks currently call
setPendingDashboardSessionAction(host, sessionId, null) unconditionally and can
clear newer pending actions; change both error-path calls to be token-aware by
passing the same token used on success (i.e., call
setPendingDashboardSessionAction(host, sessionId, tokenOrNull) so the setter
only clears when the token matches), ensuring you compare or supply the token
used when initiating the lifecycle operation (refer to the token variable used
alongside setPendingDashboardSessionAction in the success path) and apply the
same fix to the second occurrence around the other catch (the other
setPendingDashboardSessionAction call).

In `@src/multiplexer/dashboard-ops.ts`:
- Around line 70-93: The current restoreWarningLines early-returns when
result.warning is empty which drops teammateFailures; update restoreWarningLines
to always process result.teammateFailures (inspect result.teammateFailures into
failures as currently done) regardless of result.warning, then build lines =
failures.length > 0 ? failures : (warning ? [warning] : []), derive uniqueLines
from that, and only append the stale-teammates fallback ("Stale teammates remain
offline; create a new team to replace them.") when uniqueLines is empty; keep
using the same variables/function names (restoreWarningLines, result.warning,
result.teammateFailures, failures, uniqueLines) so behavior reflects teammate
failures independently and avoids adding the fallback when backend-provided
failure lines exist.
- Around line 839-853: The dashboard resume path (resumeOfflineSession)
currently posts only { sessionId } and thus drops session metadata; update
resumeOfflineSession to mirror resumeOfflineSessionWithFeedback by including the
full session seed and team in the payload when calling host.postToProjectService
(e.g., send { sessionId: session.id, session: sessionSeed } and include team:
session.team or the equivalent sessionSeed.team), so callers via
dashboardSessionActionDeps().resumeOfflineSession preserve the same session
shape as the main restore flow; modify the payload passed to
host.postToProjectService in resumeOfflineSession accordingly.

In `@src/multiplexer/persistence-methods.ts`:
- Around line 309-324: The teammates mapping currently forces active: false
which drops the real active state; change the mapping in persistence-methods.ts
(the orderedTeammates.map callback) to copy the session's active flag (e.g., use
session.active or the existing property that represents focus/active) instead of
hard-coding false so the teammates entry preserves its real active status for
the tmux statusline payload.

In `@src/multiplexer/session-actions.ts`:
- Around line 379-384: The code currently creates a teammate session before
validating that agent input is supported, causing orphaned sessions when
initialPrompt is provided but host.writeAgentInput isn't implemented; modify
createTeammateAgent to perform the initialPrompt check and throw before any
session creation: if opts.initialPrompt?.trim() then assert typeof
host.writeAgentInput === "function" (and throw the existing Error message)
before calling whatever session-creation logic uses transport.id, and only call
host.writeAgentInput after the session is successfully created; update
references to opts.initialPrompt, host.writeAgentInput, createTeammateAgent and
transport.id accordingly so the session is never created when agent input
support is missing.

In `@src/multiplexer/session-capture.ts`:
- Around line 37-39: The current hasExactAimuxSessionPreamble uses includes(...)
which can match sessionId substrings (e.g., "codex-1" inside "codex-10"); change
it to assert an exact token match by using a regex that escapes the sessionId
and requires a word-boundary or end-of-string after it (e.g., new RegExp with
\\b or (?:$) after the escaped sessionId) so the preamble text "This is an
aimux-managed session with session ID <id>" only matches when <id> is an exact
session token; update hasExactAimuxSessionPreamble to build/escape the regex and
test text with it.
- Around line 31-35: The function readSessionCaptureSample currently reads the
entire file into memory; change it to stream/partial-read the file using fs.stat
and either fs.createReadStream to read the first SESSION_CAPTURE_MAX_BYTES and a
final tail read or use fs.open + fs.read to read the first and last
SESSION_CAPTURE_MAX_BYTES directly by offset so you never load the whole file;
keep the same return format (head + "\n" + tail) and reference the same
constants and function name readSessionCaptureSample and
SESSION_CAPTURE_MAX_BYTES while ensuring you handle small files (return full
content if file size <= SESSION_CAPTURE_MAX_BYTES * 2) and close any file
descriptors or streams after reading.

In `@src/statusline-model.ts`:
- Around line 396-399: resolveCurrentTeammates is including teammates regardless
of status; update its filter to exclude inactive sessions by checking the
session status before sorting/slicing. Modify the existing chain on
data.teammates (in resolveCurrentTeammates) to include an additional filter such
as session => session.status === 'active' (or session.isActive === true if your
model uses that flag) alongside the existing parentSessionId check so only
active teammates are considered, then sort with compareTeammateSessions and
slice as before.

---

Outside diff comments:
In `@src/dashboard/pending-actions.ts`:
- Around line 315-337: settleCreatePending currently unconditionally calls
clearEntry(target, itemId) after an async wait which can remove a newer pending
action; fix by recording a unique marker when this settle flow starts (e.g.,
capture startedAt or a generated token) and, just before calling clearEntry,
verify the current pending entry for target/itemId still matches that marker
(compare the entry's createdAt or token stored when the pending action was
enqueued); only call clearEntry(target, itemId) and invoke onSettled() if the
markers match, otherwise skip clearing so newer pending actions are not removed.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 87e1c6c5-4123-401d-bf9a-d95c39bf5bff

📥 Commits

Reviewing files that changed from the base of the PR and between 2f2ea84 and 0b86517.

📒 Files selected for processing (86)
  • .gitignore
  • README.md
  • desktop-ui/src/lib/WorktreePanel.svelte
  • desktop-ui/src/stores/state.svelte.js
  • scripts/tmux-control.sh
  • src/daemon.test.ts
  • src/daemon.ts
  • src/dashboard/index.test.ts
  • src/dashboard/index.ts
  • src/dashboard/pending-actions.test.ts
  • src/dashboard/pending-actions.ts
  • src/dashboard/quick-jump.test.ts
  • src/dashboard/quick-jump.ts
  • src/dashboard/session-actions.ts
  • src/dashboard/session-registry.test.ts
  • src/dashboard/session-registry.ts
  • src/dashboard/state.ts
  • src/dashboard/ui-state-store.ts
  • src/debug-state.test.ts
  • src/debug-state.ts
  • src/fast-control.test.ts
  • src/fast-control.ts
  • src/instance-directory.test.ts
  • src/instance-directory.ts
  • src/instance-registry.ts
  • src/main.ts
  • src/metadata-server.test.ts
  • src/metadata-server.ts
  • src/multiplexer/archives.ts
  • src/multiplexer/dashboard-control.test.ts
  • src/multiplexer/dashboard-control.ts
  • src/multiplexer/dashboard-interaction.test.ts
  • src/multiplexer/dashboard-interaction.ts
  • src/multiplexer/dashboard-model.test.ts
  • src/multiplexer/dashboard-model.ts
  • src/multiplexer/dashboard-ops.test.ts
  • src/multiplexer/dashboard-ops.ts
  • src/multiplexer/dashboard-state-methods.ts
  • src/multiplexer/dashboard-tail-methods.test.ts
  • src/multiplexer/dashboard-tail-methods.ts
  • src/multiplexer/dashboard-view-methods.test.ts
  • src/multiplexer/dashboard-view-methods.ts
  • src/multiplexer/graveyard-view-model.test.ts
  • src/multiplexer/graveyard-view-model.ts
  • src/multiplexer/index.ts
  • src/multiplexer/notifications.test.ts
  • src/multiplexer/notifications.ts
  • src/multiplexer/persistence-methods.test.ts
  • src/multiplexer/persistence-methods.ts
  • src/multiplexer/runtime-lifecycle-methods.ts
  • src/multiplexer/runtime-state.test.ts
  • src/multiplexer/runtime-state.ts
  • src/multiplexer/service-state-snapshot.test.ts
  • src/multiplexer/service-state-snapshot.ts
  • src/multiplexer/services.test.ts
  • src/multiplexer/services.ts
  • src/multiplexer/session-actions.test.ts
  • src/multiplexer/session-actions.ts
  • src/multiplexer/session-capture.ts
  • src/multiplexer/session-launch.test.ts
  • src/multiplexer/session-launch.ts
  • src/multiplexer/session-runtime-core.test.ts
  • src/multiplexer/session-runtime-core.ts
  • src/multiplexer/subscreens.test.ts
  • src/multiplexer/subscreens.ts
  • src/multiplexer/worktrees.test.ts
  • src/multiplexer/worktrees.ts
  • src/paths.ts
  • src/pending-actions.ts
  • src/session-bootstrap.test.ts
  • src/session-bootstrap.ts
  • src/session-runtime.ts
  • src/session-semantics.test.ts
  • src/session-semantics.ts
  • src/statusline-model.ts
  • src/team.test.ts
  • src/team.ts
  • src/tmux/control-script.test.ts
  • src/tmux/runtime-manager.test.ts
  • src/tmux/runtime-manager.ts
  • src/tmux/statusline.test.ts
  • src/tmux/statusline.ts
  • src/tui/screens/dashboard-renderers.test.ts
  • src/tui/screens/dashboard-renderers.ts
  • src/tui/screens/overlay-renderers.ts
  • src/tui/screens/subscreen-renderers.ts

Comment thread scripts/tmux-control.sh
Comment thread src/dashboard/ui-state-store.ts
Comment thread src/debug-state.ts
Comment thread src/metadata-server.ts
Comment thread src/multiplexer/dashboard-control.ts
Comment thread src/multiplexer/persistence-methods.ts
Comment thread src/multiplexer/session-actions.ts
Comment thread src/multiplexer/session-capture.ts
Comment thread src/multiplexer/session-capture.ts
Comment thread src/statusline-model.ts
@TraderSamwise

Copy link
Copy Markdown
Owner Author

Fixed in 5d32b4d - the outside-diff pending-actions finding is addressed by token-aware settle cleanup so stale settle callbacks cannot clear newer pending actions.

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