Skip to content

feat: Board task DAG + Agent Profiles (dispatcher on Cue tick, worktree isolation, plugin-bus events, CLI parity) - #1272

Open
chr1syy wants to merge 71 commits into
RunMaestro:rcfrom
chr1syy:build/board-profiles-fullaccess
Open

feat: Board task DAG + Agent Profiles (dispatcher on Cue tick, worktree isolation, plugin-bus events, CLI parity)#1272
chr1syy wants to merge 71 commits into
RunMaestro:rcfrom
chr1syy:build/board-profiles-fullaccess

Conversation

@chr1syy

@chr1syy chr1syy commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Board and Agent Profiles

Adds the Board: a persistent, git-trackable task DAG in .maestro/board.yaml whose cards are dispatched to Agent Profiles on the existing Maestro Cue engine tick (no second timer). Encore-gated, and gated again on Cue.

Core

  • Agent Profiles (.maestro/profiles.yaml): named model / effort / role-prompt / args override bundles, resolved as profile, then base agent, then unset onto the existing spawn options. No new spawn parameters.
  • Board dispatcher: promote (all parents done), then claim up to a WIP cap, then spawn, then apply. The board is persisted before any spawn resolves, so a restart or a re-entrant tick can never double-dispatch. Retries are bounded by a circuit breaker; every attempt is recorded as an auditable CardRun.
  • Worker pool (opt-in): an agent with boardWorker: true whose working dir is inside the project can pick up role-only cards. Interactive agents are never hijacked.
  • Per-card git worktrees: a card can run in its own checkout on board/<board>/<card>, so cards dispatched in the same tick cannot collide. Provisioning goes through the single shared setupWorktreeLocal; nothing is auto-merged or auto-deleted. SSH remotes are refused rather than silently downgraded to a local checkout.
  • Full CLI parity (maestro-cli board / profile): board and card lifecycle, profile lifecycle, tick, and watch, all through the same storage modules and the same spawn path as the desktop app, so SSH remotes and profile overrides behave identically.
  • Plugin bus: four metadata-only topics (board.cardStatusChanged, board.cardCompleted, board.cardBlocked, board.decomposed) at host API 1.15.0, on top of rc's 1.14.0 (tool.executed + ui.panelPost). Titles ship; summaries, prompts, outputs, and block reasons never do. The vendored plugin-sdk contracts are bumped in lockstep (0.10.0) with the drift guard green.
  • Optional auto-decompose, off by default: one LLM pass fans a triage card into a wired child graph, capped per tick, using the editable board-decompose core prompt.

Safety

Board and profile YAML loads fail closed (an unreadable file blocks writes instead of being silently replaced by an empty board, which used to destroy data), writes are atomic temp-file+rename, and async read-modify-write callers serialize through a shared per-file write chain. Card deletion keeps the DAG referentially intact through grandparent adoption. Deleting a board with unfinished cards requires an explicit force.

UI

Kanban modal with keyboard parity (a "Move to" picker rather than ARIA drag-and-drop), multi-board switching, per-card stop, done/blocked toasts, an expandable last-run summary, and a running/ready pill in the Main Window header. Both the modal and the pill update from a board:changed push after every write; nothing polls.

Docs: docs/board.md (users), docs/agent-guides/BOARD.md (contributors), docs/cli-reference.md (flags).

Tests: shared DAG/markers/pool/types, storage, dispatcher, spawn, worktree, decompose, both CLI command suites, BoardModal, ProfilesModal, extension model, plugin events, and the SDK drift guard.

Known gaps (documented, deliberate):

  • The pool busy-set is per board, not per project: two boards in one project can hand the same agent a card concurrently. Fixing it needs a project-wide busy set; called out in both guides.
  • A tier-2 plugin cannot yet hold agents:dispatch / process:spawn for the Board; the runtime dispatch-grant seam is specified in Plans/rfc-runtime-dispatch-grants.md and dispatch stays host-owned until it exists.
  • No live output streaming for card runs; you get the exit status, completion marker, and optional summary.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Introduced the Board experience with persistent task DAGs: dispatcher ticks, WIP-limited claiming, card status transitions, optional triage auto-decomposition, and per-card isolated worktrees.
    • Added Agent Profiles with a dedicated desktop modal, role-based board worker opt-in, and full maestro-cli board / maestro-cli profile headless management (including board tick/watch).
  • Documentation

    • Added/expanded Board and Agent Profiles guides plus CLI reference and Host API event documentation; updated navigation to include the Board feature.
  • Plugin Updates

    • Bumped Host API to 1.15.0 and added metadata-only board event topics for card lifecycle updates and decomposition.

chr1syy and others added 18 commits July 10, 2026 05:52
Introduce lightweight Profiles: a named override bundle layered on an
existing base agent that overrides model, reasoning effort, role
system-prompt, and optionally extra CLI args - same binary, cwd, and SSH
config as the base agent. Profiles are the "assignee" primitive the Board
(later phases) wires cards to.

- src/shared/profiles/types.ts: AgentProfile type, pure
  resolveProfileSpawnOverrides (profile -> base agent -> undefined) mapping
  onto existing SpawnAgentOptions fields (no new spawn params), and a
  reusable validateAgentProfile guard.
- src/main/profiles/profile-storage.ts: per-project .maestro/profiles.yaml
  load/save/list/upsert/delete using the shared js-yaml dep; skips
  malformed/duplicate entries with a logged warning.
- IPC: profiles:list/upsert/delete handler + preload bridge +
  window.maestro.profiles namespace, following the Cue handler pattern.
- UI: minimal ProfilesModal (list + create by base agent/model/effort/role),
  registered with useModalLayer and select-none.
- Tests: resolution-order + validation unit tests; storage round-trip and
  malformed-skip tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduce the persistent Board: a per-project task DAG stored in
.maestro/board.yaml (human-diffable, git-trackable, mirroring the Cue and
Phase 1 Profiles conventions). This phase is the data foundation only - no
dispatcher (Phase 3) and no rich UI (Phase 4).

- src/shared/board/types.ts: pure BoardCard / CardStatus / CardRun /
  WorktreeRef / Board contracts with per-status meaning comments, plus
  validateBoardCard/validateBoard guards mirroring Phase 1's profile validator.
- src/shared/board/graph.ts: pure DAG helpers consumed by the Phase 3
  dispatcher - getEligibleCards (todo cards with all parents done), getBlockers
  (not-done parents), hasCycle (iterative 3-color DFS; self-parent = cycle,
  dangling parent id ignored). Fully unit-tested (22 cases).
- src/main/board/board-storage.ts: single owner of .maestro/board.yaml using
  the shared js-yaml dep (no new dependency). load/save/list/get + card
  mutations (addCard, updateCard, updateCardStatus, deleteCard); save rejects a
  cyclic graph via hasCycle, load skips malformed cards/boards with a logged
  warning. Adds BOARD_CONFIG_PATH to shared/maestro-paths.ts. 13 tests.
- IPC: board:list/get/addCard/updateCard/setCardStatus/deleteCard handler +
  preload bridge + typed window.maestro.board namespace, following the Cue and
  Profiles handler pattern; registered in the main process.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make the Board actually run: a board-dispatch pass on the existing Cue
engine heartbeat tick drains each project's task DAG - promote eligible
cards, spawn the assignee profile through the existing executeCuePrompt
path (SSH/custom config honored), enforce a WIP cap, and move cards to
done/blocked from completion markers (marker wins, else exit status).

- src/shared/board/cardMarkers.ts: parseCardMarkers (card-complete /
  card-block), modeled on goalMarkers.
- src/main/board/board-dispatcher.ts: pure-ish orchestrator with all
  side effects injected (testable without Electron); promote / WIP-cap
  claim / marker+exit completion / circuit breaker (2 failures) /
  stale-running reclaim.
- src/main/board/board-spawn.ts: resolve card profile -> base agent and
  run via executeCuePrompt.
- src/main/cue/cue-engine.ts: boardDispatchTick on the shared heartbeat
  tick (no second timer), one dispatcher per board, gated on BOTH
  maestroCue && board (Board requires Cue) read fresh each tick.
- src/shared/plugins/first-party.ts: BOARD_FIRST_PARTY_PLUGIN (mirrors
  Cue; dispatch/spawn stay host-owned).
- renderer: board Encore flag + default + Extensions beta tile.
- tests: cardMarkers + dispatcher lifecycle (A->B->C under cap 1, block
  marker, circuit breaker) with fakes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Visual kanban surface over the persistent task DAG (Phase 2 IPC),
dispatched on the Cue tick (Phase 3). Six status columns, a card
create/edit editor, parent-dependency badges, and manual drag-to-move.

- src/renderer/components/BoardModal.tsx: six-column kanban, live-polled
  board state, inline card editor (title/body/assignee-profile/status/
  optional worktree/parents), client-side hasCycle guard with inline
  error, HTML5 drag-to-move calling setCardStatus (coarse-pointer haptic),
  "waiting on N" blocker badges via getBlockers.
- board:create IPC: createBoard in board-storage + handler + preload +
  global.d.ts, so the UI can stand up the first board on demand.
- modalStore/AppStandaloneModals/App.tsx: boardModal state, lazy render
  gated on encoreFeatures.board, force-close when board OR maestroCue
  turns off (Board depends on Cue), entry point gated on both flags.
- Entry points: Quick Actions "Board" command (threaded onOpenBoard
  through the AppModals chain) + "Open Board" button in the Extensions
  Board tile (mirrors Pianola).
- Extensions dependency gate: BUILTIN_DEPENDENCIES (board -> maestroCue,
  first dependent flag) + isDependencyMet; ExtensionDetails disables the
  Board Enable toggle with a "Requires Maestro Cue" tooltip when Cue is off.
- Fixed the Phase 1 ProfilesModal captureException signature bug.
- Tests: BoardModal card-create + cycle-rejection interaction, createBoard
  storage, extensionModel dependency gate; updated stale BUILTIN_FEATURES
  enumeration to include board.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… Phase 5)

Round out the Board with headless CLI control, an OPTIONAL off-by-default
auto-decompose layer, structured completion-handoff metadata, and docs. The
manual Board (Phases 1-4) stays fully useful without any of the new layers.

- CLI: new `maestro-cli board` (list / show / add-card / set-status / tick) and
  `maestro-cli profile` (list / create / delete) commands, registered in
  src/cli/index.ts. They delegate to the SAME Electron-free board/profile
  storage the IPC uses (no drift). `board tick` reuses the Phase 3 pure
  dispatcher helpers (promote/claim/apply/reclaim) and the existing CLI
  spawnAgent path - honoring SSH + profile model/effort/role/args overrides - so
  no second spawn implementation. Repo try/catch-at-boundary convention.

- Auto-decompose (off by default): board-level `autoDecompose?: boolean` flag +
  new framework-free src/main/board/board-decompose.ts. Gated strictly on the
  flag; takes up to DEFAULT_AUTO_DECOMPOSE_PER_TICK (3) triage cards per pass,
  runs one LLM pass each, parses fenced/bare JSON, clamps dependsOn to
  earlier/in-range/non-self indices (always acyclic), wires children to the
  triage umbrella + siblings, and retires the triage card to done so it is never
  re-expanded. Editable prompt src/prompts/board-decompose.md registered as
  PROMPT_IDS.BOARD_DECOMPOSE. Wired into CLI `board tick` and the desktop engine
  (cue-engine maybeAutoDecompose, guarded fire-and-forget; index.ts wiring).

- Handoff: shared CARD_HANDOFF_REMINDER (one copy for CLI + desktop) prepended
  to every card prompt, asking for a `card-complete | summary` marker covering
  the four handoff questions. Summary capture already lands via applyCardResult;
  BoardModal now shows an expandable "Last run summary". Optional metadata.

- Docs: new docs/agent-guides/BOARD.md + user-facing docs/board.md (registered
  in docs.json under Encore Features), plus two CLAUDE.md key-files rows. No
  em/en-dashes; docs/releases.md untouched.

- Tests: board-decompose (flag-off never expands + cap/wiring/retirement),
  board + profile CLI round-trips, autoDecompose YAML round-trip. tsc clean,
  eslint clean, 108 board/profile/CLI/UI tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Let any opted-in agent in a board's project directory pick up cards, instead
of pinning every card to one fixed agent. The board stays per-project and
git-tracked; the "who runs it" binding changes from one agent to a free pool.

Model:
- Pool = agents with `boardWorker: true` whose cwd is inside the board's
  project dir (or a sub-folder). Opt-in via a toggle on EditAgentModal
  (default OFF, so interactive agents are never hijacked).
- A card names a role (`assigneeProfileId`) and/or pins an agent
  (`assigneeAgentId`); at least one is required. Role-only cards float to any
  free worker; pinned cards run on that agent with its own settings.
- `AgentProfile.baseAgentId` is now optional: absent = a pure pool role.
- One card per worker (tracked via `CardRun.workerAgentId`, recomputed from the
  persisted board each tick so it survives restarts). All workers busy -> the
  card stays `ready` and retries next tick (no waiting/reservation).

Dispatcher: additive `assign` dep + `claimReadyCardsPooled` + pooled tick path;
the legacy single-agent path is untouched when `assign` is absent. Pool
selection lives in one shared, pure module (`shared/board/pool.ts`) used by the
desktop engine, the CLI (`board tick`), and BoardModal.

UI: BoardModal card editor gains Role + "Pin to agent" selects and a
role/pin/pool badge; ProfilesModal base agent gains a "None (pool role)"
option; the board-worker toggle on EditAgentModal.

CLI: `board add-card --assignee <role> --assignee-agent <pin>`, and
`profile create --pool` for base-agent-less roles.

Also fixes auto-decompose to inherit a pinned agent (agent-only triage cards
would otherwise fan into children that validation drops), and a latent
getBlockers() arg-order bug in `board show`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts:
#	src/__tests__/renderer/components/Settings/Extensions/extensionModel.test.ts
#	src/renderer/components/Settings/Extensions/extensionModel.ts
#	src/renderer/stores/settingsStore.ts
#	src/renderer/types/index.ts
#	src/shared/plugins/first-party.ts
…erywhere

The toolbar pill displayed "Full Access" for a tab whose permissionMode was
never set (defaulting the DISPLAY to 'full'), but the spawn path passed the raw
undefined through to buildAgentArgs, which only grants the bypass on a literal
'full'. So Claude Code (whose --dangerously-skip-permissions lives only in
fullAccessArgs) spawned in the default permission model and had every tool call
denied - the box said Full Access while the agent was actually denied, and
goal-driven / document Auto Run deadlocked.

Introduce resolveTabPermissionMode() as the single source of truth for what an
unset permissionMode means and call it from both the toolbar (display) and the
interactive spawn path so they can no longer drift. Auto Run, which has no
active tab, now spawns with permissionMode: 'full' explicitly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…fullaccess

# Conflicts:
#	src/__tests__/renderer/hooks/useAgentExecution.test.ts
#	src/__tests__/shared/agentMetadata.test.ts
#	src/main/preload/index.ts
#	src/renderer/App.tsx
#	src/renderer/components/AppModals/AppSessionModals.tsx
#	src/renderer/components/NewInstanceModal/EditAgentModal.tsx
#	src/renderer/components/NewInstanceModal/types.ts
#	src/renderer/hooks/session/useSessionLifecycle.ts
…eload, breaker fairness (Board 10x Phase 1)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… priority (Board 10x Phase 2)

- board:changed push event replaces the 2.5s kanban poll. board-storage
  exposes setBoardSavedListener; index.ts broadcasts { projectRoot } to
  every window plus the web-desktop bridge. Storage stays host-agnostic
  so the CLI (no webContents) is unaffected.
- Cancellation: board-spawn remembers the Cue runId per card and kills it
  via stopCueRun; the dispatcher gains cancelSpawn + cancelCard and a
  cancel tombstone so the killed run's late resolution cannot re-finalize
  the card as a failure. New 'canceled' run outcome returns the card to
  todo and is excluded from the circuit breaker. board:cancelCard IPC
  routes through the live dispatcher, with a storage-only finalize
  fallback for cards left running by a previous app run. UI: stop button
  on running tiles, drag-out of the running column now refuses with an
  explanatory toast.
- Terminal transitions notify through the existing remote:notifyToast
  relay (green done / red sticky blocked, sourceAgent 'Board').
- Safe destructive actions: two-click tile delete that names how many
  dependents get re-parented, a ConfirmModal discard gate for unsaved
  editor changes on Escape/backdrop/Back, and a delete button visible on
  focus-within and always visible on coarse pointers.
- Card priority (high/normal/low, normal never serialized) drives one
  shared ready-card dispatch order for both claim paths; surfaced in the
  card editor, as a tile badge, and via `board add-card --priority`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Board 10x Phase 3)

Close the headless-parity gap so a CI box or a headless host can drive the
Board end to end without ever opening the desktop app.

New CLI surface:
  board create | rename | delete        (delete refuses non-done cards without --force)
  board update-card | remove-card       (running cards are refused / need --force)
  board watch                           (board tick on a timer, SIGINT to stop)
  profile update | show                 (update keeps the id, so cards keep resolving)

Storage gains renameBoard / deleteBoard and an options bag on createBoard
(maxInProgress, autoDecompose). The non-done guard rail lives in storage, not
the CLI, so the desktop path inherits it; board:rename / board:delete IPC,
preload, and renderer types are plumbed for the Phase 6 UI.

board tick and board watch now share one tickBoardsOnce pass. The CLI has no
handle on an in-flight card process, so remove-card --force says plainly that
the run was not canceled rather than implying a kill it cannot perform.

docs/cli-reference.md regenerated (npm run gen:cli-reference); BOARD.md and
board.md refreshed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- extract the local git worktree provisioning out of the git:worktreeSetup
  IPC handler into src/main/utils/git-worktree.ts so Auto Run and the Board
  share one implementation
- add pure branch/path naming (src/shared/board/worktree.ts) and the Board
  provisioning wrapper (src/main/board/board-worktree.ts, Electron-free so
  the CLI uses the identical path)
- spawn worktree cards with the checkout as cwd (desktop + CLI), record
  worktreePath/worktreeBranch on the CardRun, refuse isolation on SSH remotes
- add the 'Run in isolated worktree' toggle, a branch badge on card tiles,
  the branch in the completion toast and last run summary, and a
  'Worktree cards' section in docs/board.md with the merge-back commands

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…count, dispatch-grant RFC (Board 10x Phase 5)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ibility (Board 10x Phase 6)

Modal priorities: PROFILES_MODAL moved 458 -> 462, which had collided with
SSH_REMOTE (458). Profiles has to outrank BOARD_MODAL (457) because the Board's
empty state now opens it, and 458-461 were taken (SSH_REMOTE, Pianola, Cue), so
462 is the first free value above Board. A uniqueness sweep of every value in
modalPriorities.ts confirms the Board/Profiles collision is gone; three
PRE-EXISTING collisions remain and are deliberately untouched (out of scope):
725 DOCUMENT_SELECTOR/PROMPT_COMPOSER, 710 TAB_SWITCHER/AUTORUN_SETUP/SYMPHONY,
650 NEW_GROUP_CHAT/SHORTCUTS_HELP.

Accessibility and keyboard:
- role="dialog" + aria-modal + aria-labelledby on Board and Profiles modals.
- Columns are role="group" labelled with name and card count.
- Tiles are role="button" tabIndex=0 with a themed accent focus ring. Arrows
  walk the grid, Enter/Space opens the card, Delete twice removes it (same
  arm-then-confirm path as the trash button), and `m` opens the editor focused
  on its "Move to" picker. Native HTML5 drag-and-drop is untouched for pointer
  users; no custom ARIA drag surface.
- The editor's "Move to" select persists immediately via setCardStatus with the
  same guards a drop has, and updates the dirty baseline so a move is not read
  as an unsaved edit. New cards keep the plain Status select.
- Initial dialog focus and the move-picker focus both go through
  useFocusAfterRender, one-shot via a mount ref.

Running-card visibility: running tiles show attempt number, a ticking elapsed
reading (formatElapsedTime, isolated in a RunElapsed child so one label
re-renders per second), and the pooled worker. The worker name resolves through
the session store, not getAgentDisplayName(), because CardRun.workerAgentId is a
Left Bar agent id and getAgentDisplayName maps agent *type* ids. The run-details
disclosure is now available while running, not only after.

Multi-board: header switcher across all project boards plus New / inline Rename
/ Delete (confirm repeats the CLI's non-done-cards warning). The last selected
board per project is remembered in localStorage
(maestro.board.lastBoardId:<projectRoot>), the same lightweight mechanism
GitDiffViewer and the History filters use, rather than a synced setting.
The "no profiles yet" hint is now a button that opens the Profiles modal.

Profiles parity: setProfilesSavedListener mirrors the board's post-save hook,
index.ts broadcasts profiles:changed, preload exposes onProfilesChanged, and
ProfilesModal refetches on it instead of loading once and going stale.

Tests: BoardModal.test.tsx 21 passed (keyboard nav, Enter-to-open, Delete twice,
move-to IPC, running-card metadata, board switching + persistence, board delete
confirm, empty-state profiles button); new ProfilesModal.test.tsx 3 passed;
main/profiles/storage.test.ts 16 passed. tsc, eslint, prettier clean.
…Board 10x Phase 7)

Docs
- BOARD.md: fail-closed loads, atomic + serialized writes and the shared
  write-chain helper, reclaimed/canceled run outcomes, status-change diff hook,
  a new plugin-bus board events section, SSH-worktree refusal and the
  board/profiles changed-event emit sites in Gotchas, per-board busy-set caveat.
- board.md: priority / assignee-agent / worktree / runs in the YAML shape, a run
  outcome table, dispatch order, cancellation, notifications, multi-board.
- CLAUDE.md: board worktree module in the Board key-files row.
- CLAUDE-PLUGINS.md + PLUGIN-DEVELOPMENT.md: the four board.* topics and the
  1.15.0 host-API line.

plugin-sdk
- Phase 5 bumped HOST_API_VERSION and added the board topics in
  src/shared/plugins but not in the vendored SDK copy, so the drift guard was
  red (it only runs on the sdk publish workflow, which is why CI stayed green).
  Vendor the topics + payloads, move HOST_API_VERSION to 1.15.0, bump the
  package to 0.9.0.
…(Board 10x Phase 7)

The "//" comment in packages/plugin-sdk/package.json still pointed at
HOST_API_VERSION 1.13.0 after the bump to 1.15.0. Comment-only; the drift
guard does not read it.

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

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds persistent Board and Agent Profiles, including storage, dispatching, worktree isolation, CLI and renderer interfaces, plugin contracts, host API 1.15.0 updates, documentation, and automated coverage.

Changes

Board and Agent Profiles

Layer / File(s) Summary
Shared contracts and persistence
src/shared/board/*, src/shared/profiles/*, src/main/board/*, src/main/profiles/*
Defines Board, card, graph, worktree, pool, marker, and Agent Profile contracts, validation, atomic YAML persistence, dispatcher transitions, cancellation, worker assignment, and auto-decomposition.
Runtime, CLI, and IPC integration
src/main/cue/*, src/main/index.ts, src/cli/*, src/main/ipc/*, src/main/preload/*
Runs Board dispatch from Cue heartbeats, wires cancellation and notifications, adds Board/Profile CLI commands, and exposes IPC and preload APIs.
Renderer features
src/renderer/components/*, src/renderer/App.tsx, src/renderer/stores/*, src/renderer/global.d.ts
Adds Board and Profiles modals, activity status, feature dependency gating, quick actions, keyboard controls, live refresh, and Board worker opt-in.
Plugin contracts and host API
src/shared/plugins/*, packages/plugin-sdk/*
Registers the Board first-party plugin, adds four metadata-only Board event topics, and updates host API and SDK versions.
Documentation and validation
docs/*, Plans/*, src/prompts/*, src/__tests__/*
Documents Board, Profiles, CLI, plugin events, runtime dispatch planning, and validates storage, dispatch, worktrees, CLI behavior, and renderer interactions.

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

Possibly related PRs

Suggested reviewers: pedramamini, stevenmgordon

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.70% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: Board task DAGs and Agent Profiles with dispatcher, worktree, events, and CLI support.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@greptile-apps

greptile-apps Bot commented Jul 22, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds persistent Board workflows and reusable Agent Profiles. The main changes are:

  • Cue-driven DAG promotion, dispatch, retry, cancellation, and completion handling.
  • Worker-pool assignment and per-card Git worktree isolation.
  • Shared YAML storage with atomic writes and fail-closed loading.
  • Desktop, CLI, preload, IPC, and modal support for boards and profiles.
  • Metadata-only Board events for the plugin bus.

Confidence Score: 4/5

CLI Board execution can lose persisted transitions, exclude valid workers, and bypass requested worktree isolation.

  • Concurrent CLI and desktop writes can replace newer Board state with a stale snapshot.
  • CLI pool discovery omits directory fallbacks used by the desktop dispatcher.
  • Auto-decomposition always runs in the shared checkout.
  • The core dispatcher includes strong persist-before-spawn and cancellation safeguards.

src/cli/commands/board.ts and the cross-process Board storage boundary

Important Files Changed

Filename Overview
src/cli/commands/board.ts Adds full Board CLI support, but concurrent finalization can lose state, pool discovery diverges from desktop behavior, and decomposition ignores worktree isolation.
src/main/board/board-dispatcher.ts Adds promotion, claiming, retries, stale-run reclamation, cancellation tombstones, and result application.
src/main/board/board-storage.ts Adds fail-closed YAML loading, DAG validation, atomic replacement, mutations, and process-local write serialization.
src/main/board/board-spawn.ts Adds profile resolution, worker assignment, Cue execution, cancellation tracking, SSH handling, and worktree-aware card execution.
src/main/board/board-worktree.ts Adds per-card worktree provisioning through the shared Git worktree helper.
src/main/cue/cue-engine.ts Integrates Board dispatch and decomposition with the existing Cue heartbeat.

Sequence Diagram

sequenceDiagram
    participant CLI as CLI board tick
    participant Disk as board.yaml
    participant Desktop as Desktop dispatcher
    CLI->>Disk: Load snapshot A
    Desktop->>Disk: Load current board
    Desktop->>Disk: Persist transition B
    CLI->>CLI: Apply result to snapshot A
    CLI->>Disk: Replace file with snapshot A
    Note over Disk: Transition B is lost
Loading

Reviews (1): Last reviewed commit: "MAESTRO: fix stale host-API version in p..." | Re-trigger Greptile

Comment thread src/cli/commands/board.ts
Comment thread src/cli/commands/board.ts
Comment thread src/cli/commands/board.ts
chr1syy and others added 2 commits July 22, 2026 08:45
Upstream rc shipped host API 1.14.0 (tool.executed topic + ui.panelPost, Agent
Flow PR RunMaestro#1254) after this branch had already claimed 1.15.0 for the four
board.* topics. Resolution keeps 1.15.0 as the merged version with 1.14.0
folded into the history text, unions the topic catalogs and payload maps in
both src/shared/plugins and the vendored plugin-sdk copy (tool.executed first,
board.* after, identically ordered so the drift guard passes), pins the drift
test to 1.15.0, and bumps the SDK package to 0.10.0 since upstream published
its own 0.9.0 for 1.14.0. Docs that described 1.14.0 as skipped now describe
it as merged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CI (4 red shards, 4 distinct causes):
- MainPanelHeader.test.tsx: give the settings-store mock an encoreFeatures
  object; BoardStatusIndicator's selector crashed on undefined and took all
  30 header tests down with it.
- EditAgentModal.test.tsx: expect the new 19th onSave argument (boardWorker,
  false when the checkbox is untouched) in the five exact-args assertions.
- pianola-first-party-plugin.test.ts: add ['board', 'com.maestro.board'] to
  the expected first-party registry (upstream test, written before the merge
  brought the Board definition in).
- board-worktree.test.ts (Windows): realpathSync.native for the temp root;
  the plain realpath keeps 8.3 short names (RUNNER~1) which git expands,
  failing setupWorktreeLocal's same-repo comparison on worktree reuse.

Review (greptile P1s on src/cli/commands/board.ts):
- Pool parity bug FIXED: CLI worker selection now falls back projectRoot ||
  cwd like the desktop wiring, so a worker stored with only a cwd is not
  silently dropped (role-only cards no longer wedge on no-free-worker).
  Covered by a new tick test that fails without the fix.
- Cross-process save race DOCUMENTED: the write chain is process-local by
  design; BOARD.md now states it and docs/board.md no longer calls
  overlapping dispatchers 'safe' - one dispatcher per board.
- Decompose-in-projectRoot DOCUMENTED as deliberate desktop parity: it is a
  read-only planning pass; the worktree belongs to the card's own run.

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

@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

🧹 Nitpick comments (3)
src/renderer/components/ProfilesModal/ProfilesModal.tsx (1)

142-153: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider confirming profile deletion (and flagging in-use profiles).

handleDelete removes a profile immediately with no confirmation. Board cards that reference it via assigneeProfileId are then orphaned (they fall back to rendering the raw id and can no longer resolve the role at dispatch). This is inconsistent with the destructive-action guards elsewhere in this PR (board deletion confirm dialog, the tile's arm-then-delete). A confirm step, or at least a warning when the profile is still assigned to cards, would prevent silent breakage.

🤖 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/renderer/components/ProfilesModal/ProfilesModal.tsx` around lines 142 -
153, Update handleDelete in ProfilesModal to require confirmation before
deleting a profile, and warn or block when the profile is referenced by board
cards through assigneeProfileId. Reuse the existing destructive-action
confirmation and profile/card data sources where available, preserving the
current delete, error logging, exception capture, and toast behavior after
confirmation.
src/renderer/components/Settings/Extensions/extensionModel.ts (1)

82-106: 🗄️ Data Integrity & Integration | 🔵 Trivial | 🏗️ Heavy lift

Share the dependency rule across renderer and main.

This mapping is explicitly a renderer-side mirror of the main-process gate. Keeping separate definitions can let a future dependency be enabled in one layer but rejected in the other. Move the mapping to a shared module imported by both layers, or add a parity test that exercises both paths.

🤖 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/renderer/components/Settings/Extensions/extensionModel.ts` around lines
82 - 106, Centralize BUILTIN_DEPENDENCIES in a shared module consumed by both
the renderer dependency check isDependencyMet and the main-process dispatcher
gate. Remove the duplicate layer-specific mapping and update both consumers to
use the shared definition, preserving the existing board-to-maestroCue
dependency behavior.
src/cli/commands/board.ts (1)

1005-1013: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Pass customArgs through to spawnAgent() in the decompose spawn.

This CLI spawn site forwards customModel, customEffort, customEnvVars, and sshRemoteConfig but drops customArgs, so a role's extra CLI args are silently lost on the auto-decompose pass (the sibling spawnCard does pass it). overrides here can carry customArgs from resolveCliAssignment.

As per coding guidelines: "For new CLI agent spawn sites, pass sessionSshRemoteConfig, customArgs, customEnvVars, customModel, and customEffort through to spawnAgent()".

♻️ Proposed fix
 		const result = await spawnAgent(base.toolType as ToolType, projectRoot, prompt, undefined, {
 			customModel: overrides?.customModel,
 			customEffort: overrides?.customEffort,
+			customArgs: overrides?.customArgs,
 			customEnvVars: base.customEnvVars,
 			sshRemoteConfig: base.sessionSshRemoteConfig,
 			enableMaestroP: base.enableMaestroP,
 			maestroPMode: base.maestroPMode,
 			maestroPPath: base.maestroPPath,
 		});
🤖 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/cli/commands/board.ts` around lines 1005 - 1013, Update the decompose
spawn call to spawnAgent in the surrounding command flow to forward
overrides?.customArgs alongside the existing customModel and customEffort
options, preserving the resolved role-specific CLI arguments during
auto-decompose.

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 `@docs/agent-guides/BOARD.md`:
- Line 177: Update the wording in the profile editing guidance to use “drivable”
instead of “driveable”, without changing the surrounding behavior or technical
details.
- Line 180: The cross-process dispatcher documentation incorrectly describes
desktop and CLI overlap as safe; update the guidance to warn that concurrent
read-modify-write cycles can overwrite persisted card transitions. In
docs/agent-guides/BOARD.md line 180, replace “safe” with the explicit
lost-update warning; in docs/board.md line 215, make overwrite risk the primary
behavior rather than a secondary caveat; and in docs/cli-reference.md line 613,
describe overlap as unsupported.

In `@docs/board.md`:
- Line 207: Update the board tick documentation to define completion as all
cards reaching terminal states, specifically done or blocked, rather than
stopping when nothing remains to promote. Revise the repeated-tick guidance in
the board workflow example to continue while cards are running, ready, or
awaiting retry, and mention a status check only if an existing documented
command supports automation.

In `@docs/cli-reference.md`:
- Around line 591-599: Update the set-status documentation to list only the
accepted manual transitions: triage, todo, blocked, and done; remove ready and
running from the status values, and do not imply that users can set
dispatcher-managed states through this command.

In `@Plans/rfc-runtime-dispatch-grants.md`:
- Around line 77-80: Clarify the project-root grant definition to require
canonical path resolution for both the configured project root and candidate
paths, separator-aware containment that excludes similarly prefixed paths such
as “repo-evil,” and explicit symlink and traversal handling that prevents
escaping the canonical project-root boundary.

In `@src/__tests__/renderer/components/BoardModal.test.tsx`:
- Around line 489-509: Widen the elapsed-time assertion in the “shows attempt,
elapsed time and the pooled worker on a running tile” test to match any seconds
value within the minute, rather than only single-digit seconds. Keep the
assertion anchored to the existing formatted “1m …s” output so it remains
tolerant of slow rendering.

In `@src/main/utils/atomic-json-store.ts`:
- Around line 116-129: Update atomicWriteFileSync to retry renameSync on
transient EPERM or EBUSY errors using the same bounded retry limit and
synchronous backoff as atomicWriteFile. Preserve cleanup of the temporary file
and rethrow non-retryable or ultimately failed errors.

In `@src/prompts/board-decompose.md`:
- Around line 12-18: Update the child-card count instruction in the
decomposition prompt so the two-to-six requirement applies normally, while
explicitly allowing a single child card when the source card is atomic. Preserve
the existing guidance for actionable scope, dependency ordering, imperative
titles, and avoiding invented work.

In `@src/renderer/components/AppStandaloneModals.tsx`:
- Around line 427-432: The Board modal must remain unavailable unless Maestro
Cue is enabled. In src/renderer/components/AppStandaloneModals.tsx lines
427-432, update the BoardModal render condition to also require
encoreFeatures.maestroCue; in
src/renderer/components/Settings/Extensions/ExtensionDetails.tsx lines 603-624,
update the active Open Board action to require dependencyMet and otherwise
display the existing dependency hint.

In `@src/renderer/components/BoardStatusIndicator.tsx`:
- Around line 47-51: Update the catch block in BoardStatusIndicator’s
card-counting flow to distinguish known recoverable board-list errors from
unexpected IPC failures. Keep the zero-count fallback and warning for recognized
recoverable errors, but report unexpected exceptions through the project’s
Sentry exception-reporting utility or rethrow them instead of silently
converting them to zero counts.
- Around line 39-53: The refresh callback in BoardStatusIndicator must prevent
an outdated board.list result from updating counts after projectRoot changes.
Add a request-generation or cancellation guard around the asynchronous work in
refresh, and only commit setCounts results or fallback values for the currently
active request; invalidate the prior request when dependencies change or the
callback is superseded.

In `@src/renderer/hooks/session/useSessionLifecycle.ts`:
- Around line 213-215: Update the agent-save payload containing boardWorker so
it only applies that field when the optional boardWorker argument is defined,
preserving an existing true opt-in when callers omit it. Keep the current value
unchanged for undefined arguments while retaining explicit true or false values.

In `@src/shared/board/worktree.ts`:
- Around line 35-60: Update boardCardBranchName to avoid truncating boardId and
cardId to only ID_SEGMENT_LENGTH characters. Use full validated IDs or append
collision-resistant suffixes derived from the complete IDs, while preserving
sanitization, fallback values, and the existing branch/path construction used by
boardWorktreePath and buildCardWorktreeRef.

In `@src/shared/profiles/types.ts`:
- Around line 109-137: Update validateAgentProfile so every present optional
field (baseAgentId, model, effort, appendSystemPrompt, and customArgs) causes
the whole profile to return null when its value is not a string. Refactor
optionalString or its callers to distinguish absent fields from malformed
values, while preserving normalization of blank strings to absent and trimming
retained values consistently.

---

Nitpick comments:
In `@src/cli/commands/board.ts`:
- Around line 1005-1013: Update the decompose spawn call to spawnAgent in the
surrounding command flow to forward overrides?.customArgs alongside the existing
customModel and customEffort options, preserving the resolved role-specific CLI
arguments during auto-decompose.

In `@src/renderer/components/ProfilesModal/ProfilesModal.tsx`:
- Around line 142-153: Update handleDelete in ProfilesModal to require
confirmation before deleting a profile, and warn or block when the profile is
referenced by board cards through assigneeProfileId. Reuse the existing
destructive-action confirmation and profile/card data sources where available,
preserving the current delete, error logging, exception capture, and toast
behavior after confirmation.

In `@src/renderer/components/Settings/Extensions/extensionModel.ts`:
- Around line 82-106: Centralize BUILTIN_DEPENDENCIES in a shared module
consumed by both the renderer dependency check isDependencyMet and the
main-process dispatcher gate. Remove the duplicate layer-specific mapping and
update both consumers to use the shared definition, preserving the existing
board-to-maestroCue dependency behavior.
🪄 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: CHILL

Plan: Pro

Run ID: f7782068-4d68-4210-a0e7-1c741dabb069

📥 Commits

Reviewing files that changed from the base of the PR and between cb7fcf8 and 3c4ef0b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (92)
  • CLAUDE-PLUGINS.md
  • CLAUDE.md
  • Plans/board-10x-summary.md
  • Plans/rfc-runtime-dispatch-grants.md
  • docs/agent-guides/BOARD.md
  • docs/agent-guides/PLUGIN-DEVELOPMENT.md
  • docs/board.md
  • docs/cli-reference.md
  • docs/docs.json
  • packages/plugin-sdk/package.json
  • packages/plugin-sdk/src/__tests__/drift.test.ts
  • packages/plugin-sdk/src/index.ts
  • src/__tests__/cli/commands/board.test.ts
  • src/__tests__/cli/commands/profile.test.ts
  • src/__tests__/main/board/board-decompose.test.ts
  • src/__tests__/main/board/board-dispatcher.test.ts
  • src/__tests__/main/board/board-spawn.test.ts
  • src/__tests__/main/board/board-worktree.test.ts
  • src/__tests__/main/board/storage.test.ts
  • src/__tests__/main/profiles/storage.test.ts
  • src/__tests__/renderer/components/BoardModal.test.tsx
  • src/__tests__/renderer/components/MainPanel/MainPanelHeader.test.tsx
  • src/__tests__/renderer/components/NewInstanceModal/EditAgentModal.test.tsx
  • src/__tests__/renderer/components/ProfilesModal.test.tsx
  • src/__tests__/renderer/components/QuickActionsModal/commands/commandBuilders.test.ts
  • src/__tests__/renderer/components/Settings/Extensions/extensionModel.test.ts
  • src/__tests__/shared/board/cardMarkers.test.ts
  • src/__tests__/shared/board/graph.test.ts
  • src/__tests__/shared/board/pool.test.ts
  • src/__tests__/shared/board/types.test.ts
  • src/__tests__/shared/pianola/pianola-first-party-plugin.test.ts
  • src/__tests__/shared/plugins/board-first-party.test.ts
  • src/__tests__/shared/plugins/events.test.ts
  • src/__tests__/shared/profiles/types.test.ts
  • src/cli/commands/board.ts
  • src/cli/commands/profile.ts
  • src/cli/index.ts
  • src/main/board/board-decompose.ts
  • src/main/board/board-dispatcher.ts
  • src/main/board/board-spawn.ts
  • src/main/board/board-storage.ts
  • src/main/board/board-worktree.ts
  • src/main/cue/cue-engine.ts
  • src/main/cue/cue-yaml-write.ts
  • src/main/index.ts
  • src/main/ipc/handlers/board.ts
  • src/main/ipc/handlers/git.ts
  • src/main/ipc/handlers/index.ts
  • src/main/ipc/handlers/profiles.ts
  • src/main/preload/board.ts
  • src/main/preload/index.ts
  • src/main/preload/profiles.ts
  • src/main/profiles/profile-storage.ts
  • src/main/utils/atomic-json-store.ts
  • src/main/utils/git-worktree.ts
  • src/prompts/board-decompose.md
  • src/renderer/App.tsx
  • src/renderer/components/AppModals/AppModals.tsx
  • src/renderer/components/AppModals/AppSessionModals.tsx
  • src/renderer/components/AppModals/AppUtilityModals.tsx
  • src/renderer/components/AppStandaloneModals.tsx
  • src/renderer/components/BoardModal.tsx
  • src/renderer/components/BoardStatusIndicator.tsx
  • src/renderer/components/MainPanel/MainPanelHeader.tsx
  • src/renderer/components/NewInstanceModal/EditAgentModal.tsx
  • src/renderer/components/NewInstanceModal/types.ts
  • src/renderer/components/ProfilesModal/ProfilesModal.tsx
  • src/renderer/components/QuickActionsModal/QuickActionsModal.tsx
  • src/renderer/components/QuickActionsModal/commands/featureCommands.ts
  • src/renderer/components/QuickActionsModal/types.ts
  • src/renderer/components/Settings/Extensions/ExtensionDetails.tsx
  • src/renderer/components/Settings/Extensions/ExtensionsGrid.tsx
  • src/renderer/components/Settings/Extensions/ExtensionsView.tsx
  • src/renderer/components/Settings/Extensions/extensionModel.ts
  • src/renderer/constants/modalPriorities.ts
  • src/renderer/global.d.ts
  • src/renderer/hooks/session/useSessionLifecycle.ts
  • src/renderer/stores/modalStore.ts
  • src/renderer/stores/settingsStore.ts
  • src/renderer/types/index.ts
  • src/shared/board/cardMarkers.ts
  • src/shared/board/graph.ts
  • src/shared/board/pool.ts
  • src/shared/board/types.ts
  • src/shared/board/worktree.ts
  • src/shared/maestro-paths.ts
  • src/shared/plugins/events.ts
  • src/shared/plugins/first-party.ts
  • src/shared/plugins/host-api.ts
  • src/shared/profiles/types.ts
  • src/shared/promptDefinitions.ts
  • src/shared/types.ts

Comment thread docs/agent-guides/BOARD.md
Comment thread docs/agent-guides/BOARD.md Outdated
Comment thread docs/board.md Outdated
Comment thread docs/cli-reference.md
Comment thread Plans/rfc-runtime-dispatch-grants.md
Comment thread src/renderer/components/BoardStatusIndicator.tsx
Comment thread src/renderer/components/BoardStatusIndicator.tsx
Comment thread src/renderer/hooks/session/useSessionLifecycle.ts Outdated
Comment thread src/shared/board/worktree.ts
Comment thread src/shared/profiles/types.ts
chr1syy and others added 6 commits July 22, 2026 12:42
Code:
- worktree naming uses FULL sanitized ids (board/<boardId>/<cardId>), not a
  first-8 truncation: board.yaml ids are hand-editable, so 'feature-auth' and
  'feature-api' collided on the same checkout and silently defeated isolation.
  Slashes inside an id are flattened so an id can never add branch hierarchy.
  Naming tests updated + collision/slash regression tests added; docs examples
  now show full-id branches.
- validateAgentProfile fails closed on malformed optional fields: a present
  non-string (model: 42, baseAgentId: {}) rejects the whole profile instead of
  silently dropping the override and running the card with the wrong
  model/effort/role. YAML null (empty 'model:' line) still means absent.
- handleSaveEditAgent only applies boardWorker when the caller passed it, so
  an omitted optional arg can no longer clear an existing opt-in during an
  unrelated save (+ regression test).
- atomicWriteFileSync retries renameSync on EPERM/EBUSY with the same bounded
  backoff as its async twin (Atomics.wait for the dependency-free sync sleep).
- BoardStatusIndicator: request-generation guard so a stale board.list cannot
  overwrite the next project's counts, and unexpected list failures are
  reported to Sentry once per project instead of silently zeroed.
- Board modal render + Extensions 'Open Board' both require the Maestro Cue
  dependency, matching every other open path; the disabled hint names the
  missing dependency.
- board-decompose prompt (+ fallback template) makes the 2-6 child rule
  explicitly conditional on the single-card atomic exception.

Docs:
- The 'overlapping dispatchers is safe' claim is corrected everywhere it
  appeared (BOARD.md CLI section, docs/board.md, docs/cli-reference.md):
  atomic writes prevent torn files, but cross-process read-modify-write can
  lose transitions; overlap is unsupported.
- board tick completion condition is now 'every card terminal (done or
  blocked)' with board show --json as the status check, not 'nothing left to
  promote'.
- cli-reference set-status lists only the manual statuses (triage|todo|
  blocked|done) and explains that ready/running are dispatcher-owned.
- dispatch-grants RFC specifies containment semantics for project-root
  grants: canonicalization, separator-aware comparison, symlink handling.
- 'driveable' -> 'drivable'.

Tests: BoardModal elapsed-time regex widened to /1m \d+s/ (single-digit
seconds flaked under slow CI renders).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Header subtitle now reads 'card DAG · dispatched on the Cue tick' and the
card editor body placeholder drops the leading unit word, reading
'Instructions handed to the assignee agent.' (the field label above
already says 'Body / instructions').

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Board hard-gated card creation on a pre-existing Agent Profile, but
since Board Phase 6 a card is valid with either assigneeProfileId OR
assigneeAgentId. Drop the profiles.length gate so 'New card' renders on
board && !draft, and delete the 'Create an Agent Profile first' branch.
canSaveDraft still guards assignee-less saves. Removed the now-unused
getModalActions import (only caller was the deleted branch).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
notifyCard's remote:notifyToast payload now carries sessionId and a
jump-session clickAction when the run recorded a workerAgentId, mirroring
the Cue precedent in cue-notify-bridge.ts. Applies to both the done and
blocked branches (shared safeSend). Legacy profile-based runs have no
workerAgentId and keep the non-clickable toast.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Forwards projectRoot and an onProfilesUpdated(updatedList, newProfileId)
callback down to CardEditor so the upcoming inline '+ New role' quick-create
can upsert a profile and select it on the open draft. The handler syncs the
Board's own profiles state from the upsert return value and points the draft's
assigneeProfileId at the new role.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts:
#	src/__tests__/renderer/components/Settings/Extensions/extensionModel.test.ts
#	src/renderer/hooks/session/useSessionSwitchCallbacks.ts

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

Caution

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

⚠️ Outside diff range comments (2)
src/main/index.ts (2)

1599-1604: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Keep agent output out of plugin events.

The blocked-card event spreads event.outcome into a contract described as metadata-only, even though outcomes may contain agent summaries or output. Remove it or emit only explicitly allow-listed 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 `@src/main/index.ts` around lines 1599 - 1604, Update the blocked-card event
emission in the blocked branch to stop spreading event.outcome into payload;
preserve only the common metadata, or include solely explicitly allow-listed
metadata fields that comply with the event contract.

1565-1568: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Serialize all Board read-modify-write operations.

Cue dispatch and auto-decomposition bypass the queue used by Board IPC mutations, allowing stale snapshots to overwrite concurrent edits.

  • src/main/index.ts#L1565-L1568: serialize dispatcher reads and saves through the per-project Board queue.
  • src/main/index.ts#L1659-L1662: serialize decomposition reload, merge, and save through the same queue.
🤖 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/main/index.ts` around lines 1565 - 1568, Serialize both Board
read-modify-write flows through the per-project Board queue: in
src/main/index.ts lines 1565-1568, update the dispatcher’s listBoards/saveBoard
operations; in src/main/index.ts lines 1659-1662, update decomposition reload,
merge, and save operations. Ensure each flow reads the current board and
persists its changes within the same queue serialization used by Board IPC
mutations.
🤖 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 `@node_modules`:
- Line 1: Remove the host-specific node_modules symbolic link from version
control and add node_modules/ to the repository’s ignore configuration. Do not
replace it with another link unless the project explicitly requires a
repository-relative target.

---

Outside diff comments:
In `@src/main/index.ts`:
- Around line 1599-1604: Update the blocked-card event emission in the blocked
branch to stop spreading event.outcome into payload; preserve only the common
metadata, or include solely explicitly allow-listed metadata fields that comply
with the event contract.
- Around line 1565-1568: Serialize both Board read-modify-write flows through
the per-project Board queue: in src/main/index.ts lines 1565-1568, update the
dispatcher’s listBoards/saveBoard operations; in src/main/index.ts lines
1659-1662, update decomposition reload, merge, and save operations. Ensure each
flow reads the current board and persists its changes within the same queue
serialization used by Board IPC mutations.
🪄 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: CHILL

Plan: Pro Plus

Run ID: 31144c2d-2871-4d95-a8ab-a01ecf2c0c60

📥 Commits

Reviewing files that changed from the base of the PR and between 63f239e and 4d7b58c.

📒 Files selected for processing (18)
  • CLAUDE.md
  • docs/agent-guides/PLUGIN-DEVELOPMENT.md
  • node_modules
  • packages/plugin-sdk/src/__tests__/drift.test.ts
  • packages/plugin-sdk/src/index.ts
  • src/__tests__/renderer/components/Settings/Extensions/extensionModel.test.ts
  • src/__tests__/renderer/hooks/useSessionLifecycle.test.ts
  • src/main/cue/cue-engine.ts
  • src/main/index.ts
  • src/renderer/App.tsx
  • src/renderer/components/Settings/Extensions/ExtensionsView.tsx
  • src/renderer/components/Settings/Extensions/extensionModel.ts
  • src/renderer/global.d.ts
  • src/renderer/hooks/session/useSessionLifecycle.ts
  • src/renderer/hooks/session/useSessionSwitchCallbacks.ts
  • src/renderer/stores/settingsStore.ts
  • src/renderer/types/index.ts
  • src/shared/types.ts
🚧 Files skipped from review as they are similar to previous changes (12)
  • packages/plugin-sdk/src/tests/drift.test.ts
  • src/renderer/stores/settingsStore.ts
  • src/renderer/types/index.ts
  • CLAUDE.md
  • src/renderer/components/Settings/Extensions/ExtensionsView.tsx
  • docs/agent-guides/PLUGIN-DEVELOPMENT.md
  • src/renderer/hooks/session/useSessionLifecycle.ts
  • src/shared/types.ts
  • packages/plugin-sdk/src/index.ts
  • src/renderer/global.d.ts
  • src/renderer/App.tsx
  • src/main/cue/cue-engine.ts

Comment thread node_modules Outdated
chr1syy and others added 10 commits July 28, 2026 10:14
Lift the push + gh pr create body out of the git:createPR IPC handler into
a new Electron-free src/main/utils/pr-creator.ts so the Board dispatcher
(main process, no renderer) can open PRs through the same code path. The
helper owns target-branch resolution (explicit -> repo default -> main),
and git:getDefaultBranch now delegates to the exported resolveDefaultBranch
so that logic exists once as well. Logging and warning are injected sinks,
mirroring git-worktree.ts. The IPC reply shape is unchanged.
F2: 'review' is a distinct card status for work that finished but needs a
human to approve or verify it, separate from 'blocked' (agent got stuck).
Adds it to CardStatus/CARD_STATUSES (between running and blocked, so the
Review column lands between Running and Blocked) and to CardRunOutcome so
the run audit trail is honest.

cardMarkers.ts gains CARD_REVIEW_RE for <!-- maestro:card-review: reason -->
(reason optional, last match wins) plus review/reviewReason on CardMarkers,
and CARD_HANDOFF_REMINDER now teaches all three markers: fix what you safely
can and card-complete, card-review when human judgment is needed, card-block
only when genuinely stuck. The dispatcher resolves precedence.

BoardModal gets the minimal STATUS_META entry required to keep tsc green;
the Review column styling and CardEditor work follow in their own commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds the F3 data model: BoardCard.prOnDone ({ targetBranch? }) as a per-card
opt-in to open a PR when the card reaches done, and CardRun.prUrl to record
the PR that was opened. Both are structurally validated in types.ts and
dropped when malformed, so a bad value disarms the opt-in instead of silently
arming a PR. Absent prOnDone is never serialized, leaving existing board.yaml
files byte-identical.

board-storage.ts needs no change: it validates through validateBoardCard and
whole-object dumps the result.
Add the review branch to applyCardResult (block > review > complete/clean
exit), reset the trailing-failure breaker on a review outcome, widen
CardNotification.kind, and notify on the review transition. board-toast
gains a yellow sticky review payload. notifyCard no longer emits
board.cardCompleted for review cards.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds src/main/board/board-pr.ts: a pure gate (done + prOnDone + a worktree
branch on the latest run + no PR already opened), a pure stamp for run.prUrl,
an injectable orchestrator, and the fire-and-forget startCardPr entry point
that binds both to board storage and the shared gh-CLI createPullRequest.

Both transitions into done route through it: the dispatcher finalize (new
optional createCardPr dep, fired after saveAndAnnounce and never awaited) and
the manual board:setCardStatus approval move. A PR failure is warn-logged and
red-toasted and never changes the card status; success green-toasts the URL and
records it on the run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… controls

Renderer half of Board F2/F3: the Review column already renders from the
CARD_STATUSES order and the STATUS_META entry added with the types, and
moveCard already permits manual -> review and the review -> done approval,
so this adds the CardEditor "Open PR when done" toggle plus its optional
target-branch input, gated on worktree isolation and persisted to
BoardCard.prOnDone only when both toggles are on.
Extend the shared board test suites for the new `review` card status and
the per-card PR-on-done opt-in:

- types.test.ts: deep-equal locks on CARD_STATUSES (review between running
  and blocked, which drives BoardModal column order) and CARD_RUN_OUTCOMES,
  review card/run round-trip, prOnDone round-trip plus disarm-when-malformed,
  and CardRun.prUrl round-trip plus drop-when-blank.
- cardMarkers.test.ts: card-review with and without a reason, whitespace
  tolerance, last-match-wins, faithful reporting alongside complete/block
  (precedence stays the dispatcher's call), and handoff reminder coverage.
- graph.test.ts: a review parent does not unblock its children and does not
  count toward the active-card cap.

Tests only, no source changes. 69 pass in src/__tests__/shared/board/.
User docs (docs/board.md): review in the run-outcome and card-status
tables, a Review section covering the approval flow and why review does
not unblock children, the card-review marker and the block > review >
complete precedence, the yellow sticky review toast, and a new Open a PR
when the card is done section under worktree cards.

Agent guide (docs/agent-guides/BOARD.md): board-pr.ts / pr-creator.ts in
the layout, prOnDone / CardRun.prUrl in the data model, the updated
applyCardResult precedence and trailing-failure reset, and a PR on done
section recording the extraction, the gate, the enqueueBoardWrite stamp,
and the in-flight set.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
chr1syy and others added 16 commits July 28, 2026 16:14
- useSessionSwitchCallbacks.test.tsx: import RefObject as a type instead of
  relying on a global React namespace (tsconfig has no allowUmdGlobalAccess),
  per CodeRabbit.
- Remove an accidentally committed node_modules symlink (mode 120000 pointing
  to an absolute host path) from version control, and extend .gitignore so a
  bare node_modules symlink is ignored (the existing node_modules/ rule only
  matches directories), per CodeRabbit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…the card

`updateCardStatus` guarded only against the dispatcher-derived statuses
(`ready`/`running`). The `CardStatus` parameter annotation is erased at
runtime, so any other string passed over the `board:setCardStatus` IPC
channel was written straight onto the card and persisted. The load-side
validator then rejected that card as malformed and dropped it, so the
call resolved successfully while the user's card disappeared from both
memory and board.yaml.

Found by driving the packaged build over CDP: setting a bogus status on
one of two cards left the board with one card and raised no error.

Not reachable from the desktop UI, which only sends typed values, and
the CLI already validates in `board set-status`. The IPC surface did
not, and the web-desktop bridge relays it too.

Adds an allowlist check against CARD_STATUSES ahead of the existing
derived-status denylist, plus a regression test covering off-enum,
empty, wrong-case, trailing-space, and `__proto__` inputs asserting the
card survives with its previous status. Also extends the author-owned
status case list to include `review`, which was missing.
Board worktrees carry no node_modules, so this repo's .husky/pre-push
(which hard-requires node_modules/.bin/prettier and otherwise runs the
full validation suite) fails every PR-on-done card at the push step.

Adds an opt-in skipHooks flag to createPullRequest that switches the push
argv to 'push --no-verify -u origin HEAD'. The Board's defaultCreatePr is
the only caller that sets it; the manual git:createPR path keeps its
byte-identical default argv and still runs the repo's gates (Decision 2).
A failed PR-on-done attempt only warn-logged and toasted, so the reason was
gone the moment the toast cleared. Record it on the card's latest run instead:

- CardRun.prError, parsed by validateCardRun like prUrl so it round-trips.
- stampCardPrError() mirrors stampCardPrUrl (same vanished-card guard, capped
  at 500 chars), wired through the same enqueueBoardWrite queue in startCardPr.
- stampCardPrUrl clears a stale prError, so a later success wipes the reason.
- The gate is unchanged: it keys only on prUrl, so a failed attempt stays
  retryable. A failure still never changes the card's status.
Render the latest run's PR state on the card tile's existing badge row: a
link chip when prUrl is set (routed through the canonical openUrl helper)
and a compact error chip carrying the persisted prError in its tooltip
when the last attempt failed. Display only - the Task 4 gate keys on
prUrl alone, so a status round-trip already re-fires the flow.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…head

An agent that marks a card done without committing would otherwise push an
empty board/<uuid>/<uuid> branch and then fail at gh pr create, littering the
remote. createPullRequest now counts commits ahead of the resolved base
(local ref, then origin/<base>) before pushing and returns a no-commits error
without touching the remote. Scoped to skipHooks callers so the human
git:createPR path is unchanged.
…one flags

board set-status <id> done now awaits maybeCreateCardPr with the same storage
wiring startCardPr binds in the desktop host, so a headless board opens the same
pull request the IPC path opens. Awaited rather than fire-and-forget: the CLI
process exits when the command returns and would kill the push mid-flight.

Adds --pr-on-done [targetBranch] to add-card and update-card plus
--no-pr-on-done to update-card, reusing the shared CardPrOnDone shape.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fixes AB1: clicking Stop killed the agent, sent the card back to `todo`,
and the very next dispatcher tick re-promoted and re-spawned it, so Stop
did not stop.

Per the recorded decision (option A-lite, not the playbook's option B),
the cancelled card stays `todo` and gains a persisted `heldByUser` flag
that `getEligibleCards` honours, cleared when the user resumes. No new
`CardStatus` and no reuse of `triage`, which `board-decompose` consumes
on boards with `autoDecompose: true`.

- BoardCard.heldByUser plus the matching copy in validateBoardCard, so
  the hold survives that validator's whitelist rebuild on save/load
- getEligibleCards skips held cards
- applyCardCancel stamps the flag; its docstring drops the old
  self-contradicting "a cancel is a pause, not a demotion" claim
- next-tick regression test that fails when the flag is removed, plus a
  YAML round-trip test for the flag

trailingFailureCount, the tombstone-before-kill ordering in cancelCard,
and finalize are byte-identical.
… the graph tests

Task 4 of FIX-BOARD-CANCEL-HOLD-01, adapted to the Decision 1 override
(heldByUser flag on a todo card, not a triage move).

- CardRunOutcome doc: canceled no longer claims the card returns to todo
  unheld; it is held via BoardCard.heldByUser until a person resumes it.
- CardStatus doc: the todo line names its two populations (never-run and
  user-held). triage is left untouched.
- graph.test.ts: pin Decision 3 - a held card is not eligible (with and
  without done parents) and a held parent still blocks its children; add the
  missing triage-parent and held-parent getBlockers cases.

Comments and tests only, no runtime change.
Task 5 of FIX-BOARD-CANCEL-HOLD-01, implemented against the recorded
Decision 1 (hold flag on a todo card) and Decision 2 (a real Resume
button) rather than the playbook's rejected option B.

- BoardModal: the cancel toast says the card is held until the user
  resumes it, a Resume button mirrors Stop on held cards, and a held
  badge explains why the card is sitting still.
- Resume clears heldByUser through the existing updateCard IPC; the
  validator only serializes the flag when true, so false drops it.
- board:cancelCard's handler comment describes the hold.
- updateCardStatus clears the hold on a manual move, so a drag or
  maestro-cli board set-status is also a valid resume path.
Brings the Board + Agent Profiles branch up to date with upstream/rc after
upstream decomposed several monoliths into focused modules.

Conflicts resolved (12 files, 22 hunks), classified before touching:

- (a) keep-both unions x10: CLAUDE-PLUGINS.md version history, the vendored
  plugin-sdk surface, src/main/index.ts module imports and boardSpawnContext,
  git.ts imports, and src/shared/plugins/events.ts.
- (b) relocations x5: upstream moved setupIpcHandlers() to
  src/main/ipc/bootstrap/index.ts and setupProcessListeners() to
  src/main/process-listeners-wiring/. Our registerProfileHandlers() and
  registerBoardHandlers({...}) pair moved into the bootstrap module, rewired to
  deps.getCueEngine / deps.safeSend / the module logger.
- (c) judgement calls x7: HOST_API_VERSION reconciled to 1.16.0 in host-api.ts,
  the vendored SDK index.ts, the drift.test.ts pin, and the CLAUDE-PLUGINS.md
  doc line (upstream reserved 1.15.0 for this fork's Board work and took 1.16.0
  for session.activated / sessions.focus / summonable panels, so both feature
  sets survive at 1.16.0). plugin-sdk package version bumped to 0.11.0.
  .gitignore takes upstream's bare node_modules with our comment reworded.
  MainPanelHeader.tsx keeps BoardStatusIndicator plus upstream's GitPillMenu and
  drops the now-unreferenced BranchSwitcherDropdown import.

Also repaired src/main/ipc/handlers/git.ts, which auto-merged without a conflict
but was left broken: restored the getShellPath import upstream's streaming-git
path needs, and deleted the resurrected local findLocalWorktreeForBranch copy
that our side had moved to src/main/utils/git-worktree.ts.

Validation: all three tsc configs clean, prettier and ESLint clean on the
touched files, 1268 scoped tests passing across board, profiles, plugin-sdk
drift, the git handler, BoardModal, ProfilesModal, MainPanelHeader, and the
shared/main plugin suites.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
One conflict: a modify/delete on src/main/ipc/handlers/git.ts. Upstream
(0027d69) split that file into eight modules under handlers/git/; this branch
had rewritten it to delegate to the shared utils it introduced, deleting 273
lines of inline implementation so the Board's per-card worktree provisioning
and PR-on-done could not drift from the IPC handlers.

Taking either whole side loses something: upstream's deletion drops the
delegation that PR RunMaestro#1272 was reviewed with, ours drops the modular split. So
the delegation was RE-APPLIED onto upstream's new layout, at the three call
sites it originally had:

- handlers/git/worktree.ts  -> setupWorktreeLocal (169 inline lines removed)
- handlers/git/github.ts    -> createPullRequest, reply shape unwrapped so
                               renderer callers see the same object as before
- handlers/git/branch.ts    -> resolveDefaultBranch

Both shared utils (utils/git-worktree.ts, utils/pr-creator.ts) are new on this
branch and merged without conflict; their extracted logic was verified against
the inline code being replaced before swapping (same guard strings, same return
shape, byte-identical default-branch resolution).

Dead code the delegation orphaned, surfaced by tsc rather than guessed at:
findLocalWorktreeForBranch and its gitUtils import in worktree.ts, getShellPath
in github.ts, execFileNoThrow in branch.ts.

Scoped tests: 2916 passing across main/board, main/ipc and main/utils. Three
tsc configs, ESLint and prettier clean.
Five conflicts, all the same shape: RunMaestro#1362 (finding AD1) landed in rc and
appended `contextWindowSource` to the edit-agent save chain, while this branch
had appended `boardWorker` to the same signatures. Pure addition on both sides.

Both kept, with `contextWindowSource` first so rc's argument positions stay
where RunMaestro#1362 put them and `boardWorker` extends past them. Applied consistently
across all four signature sites (types.ts, AppSessionModals, EditAgentModal,
useSessionLifecycle x2) and the five `onSave` assertions in the modal test,
since a positional mismatch between any of them would typecheck and then read
the wrong argument at runtime.

Scoped tests: 182 across NewInstanceModal / useContextWindow / AppModals and
225 across main/board and the session hooks. Three tsc configs, ESLint and
prettier clean.
The merge kept both `contextWindowSource` (from RunMaestro#1362, now in rc) and
`boardWorker` on the edit-agent save chain, with contextWindowSource first. This
call site in useSessionLifecycle.test.ts passes every argument positionally but
was not itself a conflict, so git left it untouched and `false` landed in
contextWindowSource instead of boardWorker.

Exactly the hazard the merge commit warned about; I checked the four signature
sites and the five modal assertions and missed this one because it lives in a
different test file. Adds the missing `undefined, // contextWindowSource`.

Swept for other positional callers of this chain: this was the only one, the
rest pass named properties. Full renderer/hooks suite now passes (5028).
@pedramamini

Copy link
Copy Markdown
Collaborator

@chr1syy first off, thank you for this - it is a genuinely impressive contribution. The Board DAG plus Agent Profiles is a large, coherent feature, and the care shows: the decision records at the top of board-spawn.ts, the fail-closed YAML loading, persisting the board before any spawn resolves, refusing SSH remotes on worktree cards instead of silently downgrading to a local checkout, and the explicitly documented known gaps in the description. That is the kind of write-up that makes a 100-file PR reviewable at all.

I have gone through both AI reviews and the code. Status from my side:

  • Greptile and CodeRabbit: every inline thread is resolved, and your responses on the three P1s are the right calls (the CLI pool s.projectRoot || s.cwd fix, documenting the process-local write chain rather than bolting on a partial lock, and the decompose-in-projectRoot parity with the desktop path).
  • CI: all six checks green, including both the ubuntu and windows legs.
  • Gating: verified on both sides. AppStandaloneModals requires encoreFeatures.board && encoreFeatures.maestroCue, and CueEngineBoardDeps.getEncoreFeatures re-reads both flags each tick, so toggling either at runtime takes effect immediately. Good.

Two CodeRabbit findings landed as "outside diff range" notes, so they never became threads and could look unaddressed. For the record, I checked both and neither blocks:

  1. "Keep agent output out of plugin events" on the board.cardBlocked payload. This is a false positive. event.outcome is CardRunOutcome, a closed string union, not free text or agent output, so the metadata-only contract holds.
  2. "Serialize all Board read-modify-write operations". This is the same cross-process lost-update issue Greptile raised, and you have already documented it as a deliberate one-dispatcher-per-board rule in docs/board.md and BOARD.md. Fine as a documented gap.

The one thing blocking merge: conflicts

The branch is 129 commits behind rc (merge base 2bf0d698e, last branch commit Aug 11), and it no longer merges cleanly. Could you please merge or rebase onto current rc and resolve? Nine files conflict:

src/main/ipc/handlers/git/worktree.ts
src/renderer/hooks/session/useSessionLifecycle.ts
src/renderer/components/NewInstanceModal/EditAgentModal.tsx
src/renderer/components/NewInstanceModal/types.ts
src/renderer/components/AppModals/AppSessionModals.tsx
src/renderer/stores/modalStore.ts
src/shared/promptDefinitions.ts
src/__tests__/renderer/hooks/useSessionLifecycle.test.ts
src/__tests__/renderer/components/NewInstanceModal/EditAgentModal.test.tsx

Two of these need more than a mechanical resolution, so flagging them specifically:

  • src/main/ipc/handlers/git/worktree.ts is the risky one. Your PR lifts the local git worktree add path out of the IPC handler into utils/git-worktree.ts (setupWorktreeLocal), while rc has since changed the code still inlined in the handler. Taking your side wholesale would silently drop those rc changes. Please port them into the extracted setupWorktreeLocal so the Board and the handler stay on one implementation, which was the whole point of the extraction.
  • useSessionLifecycle.ts / EditAgentModal.tsx / types.ts now sit on top of Provider Failover (feat(resilience): Provider Failover to Anthropic-compatible backup endpoints #1334) and the provider-preserving tabs work in rc. Your boardWorker opt-in threading through the agent edit flow will need to be re-fitted against the reshaped agent config surface rather than replayed as-is.

Once it merges cleanly and both CI legs are green again, ping me and I will take another pass. Nothing in the code itself is holding this up.

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