--- 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_