From team-tracking-mcp
Use when you have at least one specialist in flight on a ticket and you need to keep them on track — listening to the event log, reading checkpoints, posting nudges/questions, recovering from stale locks. Pairs with `team-tracking-plan` (which got you here).
How this skill is triggered — by the user, by Claude, or both
Slash command
/team-tracking-mcp:team-tracking-superviseThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You're the supervisor. Specialists are running; your job is to catch drift early, answer their questions, and recover stalled work. You **don't** plan inline — that's offloaded to the [`team-tracking-planner`](../../agents/team-tracking-planner.md) subagent so board reading and decomposition stay out of your context. You **don't** acquire locks ([`team-tracking-execute`](../team-tracking-execut...
You're the supervisor. Specialists are running; your job is to catch drift early, answer their questions, and recover stalled work. You don't plan inline — that's offloaded to the team-tracking-planner subagent so board reading and decomposition stay out of your context. You don't acquire locks (team-tracking-execute is the specialist's protocol).
Lower-level tool reference: team-tracking-usage.
For every plan and re-plan, dispatch the planner subagent. It returns a dispatch_list you act on.
Agent(
subagent_type: "team-tracking-planner",
description: "Plan <intent> for <project>",
prompt: "<PRD or blocker context>\n\nproject: <name>",
)
Two flows:
Blocked. Pass the blocked TicketRef + the blocker's progress_summary + project name. The planner adjusts the board (split, reassign, edit briefs) and returns a new dispatch_list.The planner's final message ends with a fenced JSON block:
{
"dispatch_list": [
{ "ref": { "project": "...", "id": "..." }, "role": "implementer", "brief": "..." }
],
"notes": "..."
}
Parse that block and dispatch the entries in order. Each brief is the prompt to pass to the specialist verbatim — never modify, abbreviate, or strip its leading lines. The brief's mandatory prefix (Use skill team-tracking-execute. Before any other work, run via bash: team-tracking acquire …) is the executor's bootstrap; without it the specialist may bypass the protocol entirely. If a brief looks malformed or missing the prefix, that's a planner bug — re-spawn the planner rather than ad-libbing a fix.
If a stage's specialists depend on a prior stage finishing (e.g., code-reviewer after implementer), wait for the prior Done event before dispatching the next entry. The planner's notes field flags those dependencies.
Don't load team-tracking-plan yourself. That skill belongs to the planner agent. Loading it inline pulls board-reading and decomposition reasoning into your context — which is exactly what spawning the agent avoids.
Long-running specialists drift. They hallucinate (claim work that isn't in the diff), scope-creep (start touching files outside the subtask), or get stuck looping. You catch this through two parallel signals:
team-tracking listen background bash that wakes you on every event (message, checkpoint, progress, status change, lock change).list_board every ~5 min for anything that didn't generate an event (e.g. a specialist that has gone silent for too long).When you have at least one specialist in flight, keep one background listener running per project:
Bash(
team-tracking listen --project <P> --since <last_seen_at> --timeout-ms 300000,
run_in_background: true
) → handle_orch
last_seen_at starts at the time you dispatched the first specialist; advance it to max(events.map(e => e.at)) after each wake. The listener exits on:
events envelope — the harness surfaces the JSONL output to you on the next turn. Process the events, post any responses, re-spawn the listener.timeout — the 5-min refresh. Just re-spawn with the new cursor; not a failure signal.error — log it, surface to the user if it persists, re-spawn.This replaces the old "poll every 5–10 min" cadence. The listener delivers everything that flows through the event log without a single extra read call.
When you post_message(kind="question"), record {message_id, ticket_ref, at, sla_ms = 180_000} in your own working memory. On every listener wake, check open questions whose deadline has passed without a matching in_reply_to response — that's when to nudge:
post_message(ref, {
from: "orchestrator",
kind: "nudge",
body: "Still waiting on your take on the retry path. Stuck or just deep?",
})
A 5-min subscription timeout is not the same as an unanswered question. Don't ping a healthy worker just because the channel was quiet for 5 min — only when a question you actually asked is past its SLA.
Every ~5 min while specialists are in flight, also call list_board(project) and inspect any ticket where lock_state ∈ {in_progress, committed} for staleness:
last_activity = max(lock.acquired_at, lock.last_checkpoint?.at)
age = now - last_activity
| Age | Interpretation | Action |
|---|---|---|
| < 5 min | Healthy | Move on |
| 5–15 min | Normal for non-trivial work | Confirm update / progress_summary track the spec |
| 15–30 min | Concerning | Inspect the last checkpoint's diff. If on-spec, give it room. If drifting, prepare a corrective dispatch |
| > TTL (default 30 min) | Stale lock | Lock is recoverable. Re-acquire to claim recovered_checkpoint and re-dispatch |
For any ticket with lock_state == "committed", inspect the actual diff of the last checkpoint:
git show <lock.last_checkpoint.commit_id>
The board tells you what the specialist says it did; the diff tells you what it actually did. They don't always agree — that's the whole reason to look.
Reading update, progress_summary, the recent commit's diff, and the event log via read_events(ref, { types: ["progress", "checkpoint"] }):
progress_summary mentions modules, files, or behaviors outside the subtask spec. The diff confirms files outside the expected scope changed. Note it; budget extra time for the adversarial code review to flag it.progress events with the same progress_summary and no new checkpoint. The specialist is spinning. Try a nudge; if the next event is still spinning, plan to recover via TTL.sequenceDiagram
autonumber
participant O as Orchestrator
participant T as Tracker (event log + watcher)
participant E as Executor
Note over O,E: each side has a `team-tracking listen` background bash running
Note over E: working...
O->>T: post_message(question) [emits message event]
T-->>E: watcher fires → executor's listener exits with events envelope
E->>T: post_message(response, in_reply_to:msg_A) [emits message event]
T-->>O: watcher fires → orchestrator's listener exits with events envelope
O->>T: post_message(ack, in_reply_to:response)
T-->>E: ack delivered on next pause
Note over E: continues
Round-trip target: under 1 min of wall time. Bounded by each side's in-flight tool call duration when the message arrives.
post_message(ref, {
from: "orchestrator",
kind: "nudge" | "question",
body: "Stay within auth/ — billing/ is out of scope.",
})
→ { id: "msg_abc...", at: "2026-04-25T10:30:00Z" }
What kind to use:
nudge — directional ("stay in scope X", "stop adding tests"). Executor ACKs.question — answer expected ("what blocked the retry path?"). Executor responds. Track in your open-question bookkeeping above.If a question goes 10+ min without a reply: the executor is either deep in a long tool call or stuck. Re-read its last checkpoint diff. If drifting, prepare a corrective dispatch; if still apparently working, give one more cycle before considering recovery.
acquire_ticket returns the prior recovered_checkpoint and you can re-dispatch with corrective context.question and surface to a human in parallel.The push listener delivers status_change events for Blocked and Done transitions automatically. Periodic list_board sweeps still serve as a safety net:
Blocked tickets — read progress_summary (the executor wrote you a briefing) and act: split, reassign, or surface to the user.committed locks past TTL with the same checkpoint for too long — likely crashed; recover via re-acquire.Done subtasks — dispatch the next entry in the planner's dispatch_list if there is one. If the list is exhausted but more work remains (e.g., the parent task isn't auto-Done yet), spawn the planner again for the next pipeline stage.since. After every wake, advance to max(events.map(e => e.at)) so the broker doesn't redeliver history.Todo → In Progress yourself. It happens automatically when a specialist acquires the lock.npx claudepluginhub razvanrotaru/team-tracking-plugin --plugin team-tracking-mcpCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.