---
description: Orchestrates parallel Claude Code agents across Verkada-Backend-{1-10} repos. Manages ticket queues, agent lifecycle, CI monitoring, stuck detection, and continuous 1-minute heartbeat supervision.
category: verkada
---
flowchart TD
_HEADER_["
vmanager
Orchestrates parallel Claude Code agents across Verkada-Backend-{1-10} repos. Manages ticket queues, agent lifecycle, CI monitoring, stuck detection, and continuous 1-minute heartbeat supervision.
"]:::headerStyle
classDef headerStyle fill:none,stroke:none
subgraph _MAIN_[" "]
%% Phase 0: Discovery
subgraph Discovery["Phase 0: Discover Free Repos"]
REPOS_JSON[Run repos -j
Get All Repo Status] --> FILTER_FREE{Filter
is_free: true}
FILTER_FREE -->|Candidates| TMUX_CHECK[tmux list-panes
Verify No Claude Running]
FILTER_FREE -->|None free| NO_FREE([No Free Repos
Report Occupied])
TMUX_CHECK --> FREE_LIST[Free Repo List
zsh confirmed]
end
%% Phase 1: Input Resolution
subgraph InputRes["Phase 1: Resolve Input"]
FREE_LIST --> INPUT_TYPE{Input
Type?}
INPUT_TYPE -->|PR number| RESOLVE_PR[gh pr view
Get Branch Name]
INPUT_TYPE -->|Linear ticket| RESOLVE_LINEAR[linear-cli
Get Branch Name]
INPUT_TYPE -->|trckr ticket| RESOLVE_TRCKR[trckr
Get Branch Name]
INPUT_TYPE -->|Branch name| USE_BRANCH[Use Directly]
RESOLVE_PR & RESOLVE_LINEAR & RESOLVE_TRCKR & USE_BRANCH --> BRANCH_READY[Branch Name
Resolved]
end
%% Phase 2: Prepare + Launch
subgraph Launch["Phase 2: Prepare + Launch Agent"]
BRANCH_READY --> RESET_REPO[Reset to Master
git checkout master
git pull; reset]
RESET_REPO --> CHECKOUT[Checkout Branch
git fetch + checkout]
CHECKOUT --> LAUNCH_CLAUDE[Launch Claude
clauded]
LAUNCH_CLAUDE --> VERIFY_RUNNING{Claude
Running?}
VERIFY_RUNNING -->|Version number| WARMUP[Warm Up
send /v:context]
VERIFY_RUNNING -->|Still zsh| LAUNCH_CLAUDE
WARMUP --> SEND_TASK[Send Task
/v:one-shot]
end
%% Phase 3: Heartbeat
subgraph Heartbeat["Phase 3: Heartbeat Monitor (1-min loop)"]
SEND_TASK --> START_HEARTBEAT[Set /reminder
1-min Recurring]
START_HEARTBEAT --> HB_CHECK[Check All
Active Repos]
HB_CHECK --> HB_TMUX[tmux capture-pane
Read Recent Output]
HB_TMUX --> HB_CI[pr-stat
Check CI Status]
HB_CI --> HB_TRIAGE{Agent
Status?}
HB_TRIAGE -->|Healthy| HB_NEXT[Next 1-min
Heartbeat]
HB_NEXT --> HB_CHECK
HB_TRIAGE -->|Stuck| STUCK_HANDLER
HB_TRIAGE -->|CI Failed| CI_FAIL_HANDLER
HB_TRIAGE -->|CI Green| ROTATE
HB_TRIAGE -->|Agent Exited| RELAUNCH
end
%% Phase 4: Stuck Detection + Nudge
subgraph StuckDetect["Phase 4: Stuck Agent Handling"]
STUCK_HANDLER[Detect Stuck Agent
Idle or Error] --> NUDGE_1[Nudge via
send_to_session.py]
NUDGE_1 --> NUDGE_CHECK{Responded?}
NUDGE_CHECK -->|Yes| HB_NEXT
NUDGE_CHECK -->|No| NUDGE_2[Nudge via
raw tmux send-keys]
NUDGE_2 --> NUDGE_CHECK_2{Responded?}
NUDGE_CHECK_2 -->|Yes| HB_NEXT
NUDGE_CHECK_2 -->|No| ESC_ENTER[Escape + Enter
Clear Stale Prompt]
ESC_ENTER --> ESC_CHECK{Responded?}
ESC_CHECK -->|Yes| HB_NEXT
ESC_CHECK -->|No| FORCE_EXIT[Force Exit
Kill + Relaunch]
FORCE_EXIT --> RELAUNCH
end
%% Phase 5: CI Handling
subgraph CIHandle["Phase 5: CI Handling"]
CI_FAIL_HANDLER[CI Failed] --> TELL_AGENT[Tell Agent
to Fix]
TELL_AGENT --> HB_NEXT
RELAUNCH[Relaunch Agent] --> LAUNCH_CLAUDE
end
%% Phase 6: Rotation
subgraph Rotation["Phase 6: Rotate to Next Ticket"]
ROTATE[CI Green
PR Ready] --> EXIT_CLAUDE[Send /exit
to Claude]
EXIT_CLAUDE --> VERIFY_EXIT{Claude
Exited?}
VERIFY_EXIT -->|zsh| QUEUE_CHECK{More Tickets
in Queue?}
VERIFY_EXIT -->|Still running| EXIT_CLAUDE
QUEUE_CHECK -->|Yes| NEXT_TICKET[Next Ticket
from Queue]
QUEUE_CHECK -->|No| ALL_DONE([All Tickets
Complete])
NEXT_TICKET --> RESET_REPO
end
%% Queue Management sidebar
subgraph Queue["Queue Management"]
QUEUE_INIT[Ordered Ticket List
1 per repo] --> QUEUE_TRACK[Track Status
per Repo]
QUEUE_TRACK --> QUEUE_SCOPE[Agent Scope Ends
at CI Green]
QUEUE_SCOPE --> QUEUE_USER[User Handles
Post-CI Steps]
end
click REPOS_JSON "#" "**Run repos -j**\nGet status of all Verkada-Backend-{1-10} repos as JSON.\n`repos -j --no-color`\nParse output for `is_free: true` entries.\nThis is the starting point for finding available repos."
click FILTER_FREE "#" "**Filter Free Repos**\nFilter JSON output for repos where `is_free: true`.\nA repo shows free when:\n- On master with no work\n- No uncommitted changes\n- No unpushed commits"
click TMUX_CHECK "#" "**Verify No Claude Running**\nEven if repos reports free, verify tmux:\n`tmux list-panes -t 'Verkada-Backend-N' -F '#{pane_current_command}'`\n- `zsh` = truly free\n- Version number (e.g. `2.1.47`) = Claude running, skip"
click NO_FREE "#" "**No Free Repos**\nAll repos are occupied. Report:\n- Which repos are busy\n- What branches they're on\n- Current agent status\nStop and wait for a repo to free up."
click FREE_LIST "#" "**Free Repo List**\nConfirmed free repos with both conditions met:\n1. `repos -j` shows `is_free: true`\n2. tmux pane shows `zsh`\nPick the first available for the next task."
click INPUT_TYPE "#" "**Input Type?**\nDetermine the type of input provided:\n- PR number or URL\n- Linear ticket ID (e.g. ACBE-6164)\n- trckr ticket (e.g. VERKADA-123)\n- Bare branch name\n- No argument = error, ask user"
click RESOLVE_PR "#" "**Resolve PR to Branch**\n`gh pr view NUM --repo verkada/Verkada-Backend --json headRefName --jq '.headRefName'`\nExtracts the branch name from a PR number or URL."
click RESOLVE_LINEAR "#" "**Resolve Linear Ticket**\n`linear-cli issue get ACBE-6164 --json | jq -r '.branchName'`\nExtracts the branch name from a Linear ticket ID."
click RESOLVE_TRCKR "#" "**Resolve trckr Ticket**\nGet branch name from trckr ticket.\nOr create a new branch if none exists.\nAlways use `--project VERKADA` for Verkada tickets."
click USE_BRANCH "#" "**Use Branch Directly**\nBare branch name provided.\nUse it as-is for checkout."
click BRANCH_READY "#" "**Branch Name Resolved**\nBranch name extracted from input.\nReady to prepare repo and launch agent."
click RESET_REPO "#" "**Reset to Master**\nAlways start from clean master:\n`tmux send-keys -t 'Repo' 'git checkout master; git pull; reset' Enter`\nSleep 8 seconds for git operations to complete.\nVerify with `tmux capture-pane`."
click CHECKOUT "#" "**Checkout Branch**\n`git fetch origin BRANCH`\n`git checkout BRANCH`\nSleep 5 seconds.\nVerify checkout succeeded via capture-pane.\nError if branch does not exist on remote."
click LAUNCH_CLAUDE "#" "**Launch Claude**\nSend `clauded` to tmux pane.\n`clauded` is an alias for:\n`claude --dangerously-skip-permissions --add-dir /tmp ...`\nSleep 8 seconds, then verify pane shows version number."
click VERIFY_RUNNING "#" "**Claude Running?**\nCheck `tmux list-panes` output:\n- Version number (e.g. `2.1.47`) = Claude active\n- `zsh` = failed to launch, retry"
click WARMUP "#" "**Warm Up with Context**\nSend `/v:context` (with optional ticket ID):\n`send_to_session.py Repo '/v:context ACBE-6164'`\nSleep 30 seconds for context loading.\nThis loads ticket info, codebase context, and rules."
click SEND_TASK "#" "**Send Task**\nAlways use `/v:one-shot` as the default workflow.\n`send_to_session.py Repo '/v:one-shot'`\nNever hand-craft task instructions.\n/v:one-shot runs: Preflight, Context, Implement,\nPush + CI, Staging Test, Council Review."
click START_HEARTBEAT "#" "**Set 1-min Heartbeat**\nMANDATORY after any agent launch.\n`/reminder Check agent status across all active repos every 1 minute`\nThe heartbeat chain must NEVER break.\nEvery check-in sets the next timer.\nYou are a supervisor process."
click HB_CHECK "#" "**Check All Active Repos**\nFor each repo with an active agent:\n1. tmux pane status (running or exited?)\n2. Recent output (progress or stuck?)\n3. CI status (green, red, pending?)\nTake action based on findings."
click HB_TMUX "#" "**Read Recent Output**\n`tmux capture-pane -t 'Repo' -p | tail -10`\nLook for:\n- Active spinner = working\n- Error messages = needs attention\n- Idle prompt = possibly stuck\n- Past-tense spinner = may need Escape+Enter"
click HB_CI "#" "**Check CI Status**\n`cd ~/verkada/Repo`\n`pr-stat 2>&1 | head -20`\nAlways run from within the repo directory.\nChecks PR status, CI checks, review state."
click HB_TRIAGE "#" "**Agent Status Triage**\nClassify each agent:\n- **Healthy**: active spinner, making progress\n- **Stuck**: idle prompt, same output repeatedly\n- **CI Failed**: pr-stat shows red checks\n- **CI Green**: all checks passing, PR ready\n- **Agent Exited**: pane shows zsh unexpectedly"
click HB_NEXT "#" "**Next Heartbeat**\nSet the next 1-minute timer.\nKeep heartbeats at 1-min intervals always.\nNever extend beyond 1 min, even during CI waits.\nThe heartbeat chain must never break."
click STUCK_HANDLER "#" "**Detect Stuck Agent**\nSigns of a stuck agent:\n- Idle at prompt (no spinner, shows > with no activity)\n- Error message visible in pane\n- Same output for multiple check-ins\n- Waiting for user input it cannot get"
click NUDGE_1 "#" "**Nudge via send_to_session.py**\nFirst attempt:\n`send_to_session.py Repo 'Continue with the task. If blocked, try a different approach.'`\nThis is the preferred method for sending to Claude."
click NUDGE_CHECK "#" "**Did Agent Respond?**\nCheck if the agent resumed work after nudge.\nLook for active spinner or new output."
click NUDGE_2 "#" "**Nudge via raw tmux**\nFallback if send_to_session.py fails with 'not in a mode':\n`tmux send-keys -t 'Repo' 'Continue working.' Enter`"
click NUDGE_CHECK_2 "#" "**Second Response Check**\nVerify agent resumed after raw tmux nudge."
click ESC_ENTER "#" "**Escape + Enter**\nThe #1 nudge technique for stuck prompts.\nClears stale prompt state and resubmits queued messages:\n`tmux send-keys -t 'Repo' Escape`\n`sleep 1`\n`tmux send-keys -t 'Repo' Enter`"
click ESC_CHECK "#" "**Responded After Escape+Enter?**\nIf still unresponsive after all nudge techniques,\nforce exit and relaunch with clearer instructions."
click FORCE_EXIT "#" "**Force Exit**\nAgent is truly stuck. Kill and relaunch:\n1. Send `/exit` (Enter twice for autocomplete)\n2. Verify exit (pane shows zsh)\n3. Relaunch with adjusted instructions"
click CI_FAIL_HANDLER "#" "**CI Failed**\nCI checks are red.\nTell agent to investigate and fix:\n`send_to_session.py Repo 'CI is failing. Check pr-stat and fix the issues.'`\nAfter council fixes, re-run the same testing strategy."
click TELL_AGENT "#" "**Tell Agent to Fix**\nSend specific CI failure info to the agent.\nAgent should investigate, fix, push, and watch CI again.\nAfter fixes, agent must re-run original tests."
click RELAUNCH "#" "**Relaunch Agent**\nAgent exited unexpectedly or was force-killed.\nLoop back to Launch Claude step.\nMay need to reset repo first if in bad state."
click ROTATE "#" "**CI Green - PR Ready**\nAll CI checks passing.\nAgent scope ends here.\nTime to rotate to next ticket.\nUser handles: ticket status, review requests, Slack, merge."
click EXIT_CLAUDE "#" "**Send /exit to Claude**\n`send_to_session.py Repo '/exit'`\nNote: /exit triggers autocomplete in tmux.\nMay need to send Enter twice.\nCritical: verify exit before resetting repo."
click VERIFY_EXIT "#" "**Claude Exited?**\n`tmux list-panes` must show `zsh`.\nNEVER reset a repo while Claude is still running.\nRetry /exit if still shows version number."
click QUEUE_CHECK "#" "**More Tickets?**\nCheck the ordered ticket queue.\nIf more tickets remain, pick the next one.\nIf queue is empty, all work is done."
click NEXT_TICKET "#" "**Next Ticket from Queue**\nPick the next ticket in the ordered queue.\nResolve to branch name.\nLoop back to repo preparation."
click ALL_DONE "#" "**All Tickets Complete**\nQueue is empty. All repos are free.\nAll PRs created and CI green.\nUser handles post-CI: reviews, merges, ticket status."
click QUEUE_INIT "#" "**Ordered Ticket List**\nWhen plowing through a queue:\n- Maintain an ordered list of tickets\n- Assign one ticket per repo\n- Track which repo has which ticket"
click QUEUE_TRACK "#" "**Track Status per Repo**\nFor each active repo track:\n- Which ticket is assigned\n- Current agent state\n- CI status\n- PR link"
click QUEUE_SCOPE "#" "**Agent Scope Ends at CI Green**\nAgents implement, push, create PRs, watch CI.\nAgent does NOT:\n- Mark tickets done\n- Request reviews\n- Post to Slack\n- Update ticket status\nIntercept if agents try these actions."
click QUEUE_USER "#" "**User Handles Post-CI**\nEverything after CI green is user scope:\n- Ticket status changes\n- Review requests\n- Slack posting\n- Merging PRs"
classDef discovery fill:#d1ecf1,stroke:#7ec8d8
classDef input fill:#e8daef,stroke:#b07cc6
classDef launch fill:#ffeaa7,stroke:#e0c040
classDef heartbeat fill:#fff3cd,stroke:#f0c040
classDef stuck fill:#f8d7da,stroke:#e06070
classDef ci fill:#dfe6ff,stroke:#5b7bce
classDef rotation fill:#d4edda,stroke:#5cb85c
classDef queue fill:#e0e0e0,stroke:#999999
classDef done fill:#d4edda,stroke:#5cb85c
classDef blocked fill:#f8d7da,stroke:#e06070
class REPOS_JSON,FILTER_FREE,TMUX_CHECK,NO_FREE,FREE_LIST discovery
class INPUT_TYPE,RESOLVE_PR,RESOLVE_LINEAR,RESOLVE_TRCKR,USE_BRANCH,BRANCH_READY input
class RESET_REPO,CHECKOUT,LAUNCH_CLAUDE,VERIFY_RUNNING,WARMUP,SEND_TASK launch
class START_HEARTBEAT,HB_CHECK,HB_TMUX,HB_CI,HB_TRIAGE,HB_NEXT heartbeat
class STUCK_HANDLER,NUDGE_1,NUDGE_CHECK,NUDGE_2,NUDGE_CHECK_2,ESC_ENTER,ESC_CHECK,FORCE_EXIT stuck
class CI_FAIL_HANDLER,TELL_AGENT,RELAUNCH ci
class ROTATE,EXIT_CLAUDE,VERIFY_EXIT,QUEUE_CHECK,NEXT_TICKET rotation
class QUEUE_INIT,QUEUE_TRACK,QUEUE_SCOPE,QUEUE_USER queue
class ALL_DONE done
end
style _MAIN_ fill:none,stroke:none,padding:0
_HEADER_ ~~~ _MAIN_