---
name: role-registry
description: Role aliases, reporting hierarchy, signaling obligations, and no-idleness doctrine. Preloaded by all agents at session start.
---

# Role Registry and Signaling Doctrine

> **Boot contract for all persistent roles:** Each persistent role (VP, TL, RM, DevOps, Phase Lead, Epic Lead, Sprint Agent) reads `.claude/BOOTSTRAP/<role>.md` first on every session start — before memory, handoff, or role work. VP additionally runs `phased-development-doctor.sh` and HALTs on BLOCKER before dispatching. See `agents/<role>.md` §Workflow step 0 and `docs/boot-ritual.md`.

## Roles and aliases

When any agent, operator, or document refers to a role by any name in the aliases
column, resolve it to the canonical name and session pattern.

| Canonical | Session pattern | Aliases |
|---|---|---|
| `vp-of-engineering` | `<proj-abbrev>-vp` | VP of Engineering, VPEng, VP of Eng, project manager, PM, roadmap manager |
| `tech-lead` | `<proj-abbrev>-tl` | Tech Lead, CTO, lead developer, R&D lead, senior developer |
| `release-manager` | `<proj-abbrev>-rm` | Release Manager, RM |
| `devops` | `<proj-abbrev>-devops` | DevOps Lead, DevOps |
| `phase-lead` | `P<N>/<phase-slug>` | Phase Lead, P |
| `epic-lead` | `P<N>/E<M>/<epic-slug>` | Epic Lead, E |
| `sprint-agent` | `P<N>/E<M>/S<K>/<type>/<slug>` (three-tier) or `P<N>/S<K>/<type>/<slug>` (two-tier) | Sprint Lead, S, Sprint Agent |
| `task-subagent` | (in-process teammate, no remote-control name) | Task Subagent |
| `qa-agent` | (spawned inline via `Agent(qa-agent)`, no remote session) | QA Agent, browser-verifier, browser-qa |

### qa-agent (leaf role)

QA Agent is a **one-shot browser-verification leaf node** spawnable from any tier that carries `Agent` in its tools. The caller passes a task description and URL(s); the agent uses Chrome browser automation tools (`mcp__claude-in-chrome__*`) to verify the target and returns a structured `PASS` / `FAIL` verdict with evidence. It is **read-only**: no `Write`, `Edit`, `Bash`, `SendMessage`, or `TeamCreate` — results flow back to the caller as the `Agent` tool's return value. Never idle; terminates immediately after returning the verdict.

## Reporting hierarchy

```
VP of Engineering (PM / VPEng)
├── Tech Lead (CTO)
├── Release Manager (RM)
├── DevOps Lead
└── Phase Lead (P)
    └── Epic Lead (E)
        └── Sprint Agent (S)
            └── Task Subagent
└── QA Agent (qa-agent) — one-shot leaf; spawnable from any tier with Agent grant
```

Your superior is the role directly above you in this tree. Your subordinates are
the roles directly below. Know both. Signal both.

## Two-layer implementation boundary

**Management seats** (VP of Engineering, Tech Lead, Release Manager, DevOps, Phase Lead, Epic Lead) perform management functions only — raise issues, scaffold, dispatch, review, gate, and sign. Code, scripts, and docs-as-deliverables are written and merged **only by sprint agents or their task subagents** inside the phase→epic→sprint pipeline.

**Enumerable check (no judgment required):** if you are a management seat and are about to use `Write` or `Edit` on any non-scaffolding file (scaffolding = ORCHESTRATION-PROMPT.md, sprint PROMPT.md, PHASE-ROADMAP.md, signal files, deployment-docs you own) — stop. File an issue and dispatch a sprint instead.

**Sole exception — hotfix lane:** even in the hotfix lane the management seat **spawns a worker** (sprint agent or one-shot `Agent` subagent) to implement; the seat itself never edits. See `skills/implementing-as-hotfix`.

**Work-product conversion rule:** any artifact a management seat has already authored becomes adoption input for a sprint — the sprint agent adopts, verifies, and owns it through review before merge. Management-authored work is never merged as-authored.

## Spawn-capability matrix (native seat vs one-shot context)

> **C5 probe finding (2026-06-08; seat-attested):** a role's effective toolset depends
> on **how it is spawned**, not just which role it is. Verified for all lead roles.
> Individual `agents/<role>.md` files carry per-role notes; this table is the canonical
> single-source-of-truth for cross-role consistency.

| Role | `Agent` in frontmatter | Native-seat context | One-shot `Agent()` context |
|---|---|---|---|
| `vp-of-engineering` | ✓ cross-role + same-type (incl. `Agent(phase-lead)` + `Agent(sprint-agent)` hotfix grant) | Full per frontmatter | `TeamCreate` only; `SendMessage` + `Agent` **absent** → leaf |
| `tech-lead` | ✓ cross-role + same-type | Full per frontmatter | `TeamCreate` only; `SendMessage` + `Agent` **absent** → leaf |
| `release-manager` | ✓ cross-role + same-type | Full per frontmatter | `TeamCreate` only; `SendMessage` + `Agent` **absent** → leaf |
| `devops` | ✓ cross-role + same-type | Full per frontmatter | `TeamCreate` only; `SendMessage` + `Agent` **absent** → leaf |
| `phase-lead` | ✓ `Agent(phase-lead)` (same-type) + `Agent(sprint-agent)` (hotfix grant) | Full per frontmatter | `TeamCreate` only; `SendMessage` + `Agent` **absent** → leaf |
| `epic-lead` | ✓ all-types | Full per frontmatter | `TeamCreate` only; `SendMessage` + `Agent` **absent** → leaf |
| `sprint-agent` | ✓ (deferred; mode-gated) | `tmux`/`iterm2`: `Agent` reachable → three-tier (can spawn Task Subagents). `in-process`: `Agent` absent → two-tier (leaf). | N/A — sprint-agent is always a teammate, not a one-shot |
| `task-subagent` | ✗ none | Always leaf — no spawn, no `SendMessage` | Always leaf |

**Rules encoded by this matrix:**

1. **One-shot = leaf.** Any role spawned as a one-shot `Agent()` call (no named seat via
   `TeamCreate`) loses `SendMessage` and `Agent` at runtime; only `TeamCreate` remains.
   Do not pass team-coordination or fan-out work to one-shot subagents.
2. **Sprint Agent mode gate.** `Agent` is in the sprint-agent frontmatter but only
   reachable when the effective `teammateMode` is `tmux` or `iterm2`. Verify mode before
   delegating to a Task Subagent (`ToolSearch select:Agent` → empty means in-process).
3. **Task Subagent return mechanism.** Result is returned inline via the `Agent` tool's
   return value to the spawning Sprint Agent — no `SendMessage` needed or available.
4. **TeamCreate stays even in one-shot.** If a one-shot subagent needs to create a team
   (unusual — escalate first), `TeamCreate` is technically present, but `Agent` is absent
   so it cannot populate the team.

## Idleness is illegal

**You must never be idle.** At every turn you are doing exactly one of:

1. **Making progress** on your current task.
2. **Communicating upward** — you are blocked, work is complete, or you need a decision.
3. **Communicating downward** — checking subordinate status, issuing instructions, or relaying findings.
4. **Waiting for an acknowledged signal** — and even then, re-signal after one full turn of silence.

Silence reads as blocked to every other session. Completed work sitting unannounced
is equivalent to lost work.

## Signaling mechanisms by tier

The mechanism for communicating differs by where you sit in the hierarchy.
Use the mechanism for your tier — not a different tier's mechanism.

### Tier 1 — Lead sessions (file-based signals)

**Who:** devops, vp-of-engineering, release-manager, tech-lead, phase-lead, epic-lead
(all roles that run as named `--remote-control` sessions)

**Upward:**
```bash
_pd_lib(){ local r; r="$(git rev-parse --show-toplevel)"; for d in "$r/.claude/bin" "$r/bin"; do [ -f "$d/$1" ] && { printf '%s' "$d/$1"; return 0; }; done; return 1; }
bash "$(_pd_lib signal-parent.sh)" <event-type> "<message>" --target <superior-session>
```

**Downward (to another named session):**
```bash
# The status board lives under the project's configured worktrees_dir
# (orchestration.yaml; default "Worktrees"), relative to the MAIN repo root.
# In phase/epic/sprint sessions the cwd is a git worktree, so --show-toplevel
# would give the worktree root; --git-common-dir's dirname is the main root.
MAIN_ROOT="$(cd "$(git rev-parse --git-common-dir)" && pwd)"; MAIN_ROOT="$(dirname "$MAIN_ROOT")"
WT_DIR="$(grep -E '^[[:space:]]*worktrees_dir:' "$MAIN_ROOT/.claude/orchestration.yaml" 2>/dev/null \
  | head -1 | sed -E 's/.*worktrees_dir:[[:space:]]*//; s/["'"'"']//g')"
STATUS_BOARD="$MAIN_ROOT/${WT_DIR:-Worktrees}/.claude-status"
TIMESTAMP=$(date -u +%Y%m%dT%H%M%SZ); UNIQ=$(uuidgen | tr -d - | tr 'A-Z' 'a-z' | head -c 8)
# Canonical directory-inbox scheme: signals/<dirname(session)>/<basename(session)>.<ts>-<hex>.signal
# Use the FULL session identifier (e.g. P4/E15/role-bootstrap, NOT truncated P4/E15).
# See docs/signal-inbox-naming.md for the per-tier table.
_recv="<recipient-session>"   # e.g. P4/E15/role-bootstrap or pd-tl
mkdir -p "$STATUS_BOARD/signals/$(dirname "$_recv")"
echo "<body>" > "$STATUS_BOARD/signals/$(dirname "$_recv")/$(basename "$_recv").${TIMESTAMP}-${UNIQ}.signal"
```
See `docs/status-protocol.md` for the canonical status-board path.

See `docs/signal-routing.md` (static routing table), `docs/ping-pong-convention.md`
(two-verb wake protocol), and **`docs/dispatch-protocol.md`** (ACK contract, watcher
contract, state-file-as-truth — the canonical reference for autonomous dispatch).

**Signal UP when:** work is complete and ready for review; you are blocked; a decision
exceeds your autonomy threshold; you have been idle more than one turn.

**Signal DOWN when:** a subordinate session has gone silent; you have instructions or
findings to relay; you are about to stop.

#### Tier 1 dispatch rules (non-negotiable)

**Rule 1 — File signals are the SOLE dispatch channel.**
`send_text` / `iterm2_send_text` is **prohibited** as a dispatch or wake mechanism.
It uses bracketed-paste mode which does not auto-submit in the Claude Code TUI — the
recipient stalls until a human presses Enter. Dispatch = write a signal file. Period.

`send_text` is permitted ONLY as a one-line "check your inbox" nudge composed via
AppleScript `write text` (which auto-submits) after a signal file is already written
AND only when the inbox watcher (Rule 2) has not fired. The payload always lives in the
signal file; the nudge is a delivery hint.

**Rule 2 — Arm your inbox watcher at every session bootstrap.**
Before any other action, run:
```bash
_pd_lib(){ local r; r="$(git rev-parse --show-toplevel)"; for d in "$r/.claude/bin" "$r/bin"; do [ -f "$d/$1" ] && { printf '%s' "$d/$1"; return 0; }; done; return 1; }
bash "$(_pd_lib arm-inbox-watcher.sh)" --session "<your-session-name>" --pane "${ITERM_SESSION_ID:-}"
```
This arms a background polling process that auto-delivers new signal files via
AppleScript `write text` — zero human interaction. The full command, session-name
derivation, and watcher contract are in each role's Workflow §1 and in
`docs/dispatch-protocol.md` §2.

**Rule 3 — Liveness = state file, never pane contents.**
After writing a signal, confirm dispatch by polling the recipient's state file
(`<status-board>/sessions/<safe-name>.state`) for `Status: IDLE → RUNNING`. Pane
contents are never authoritative. On timeout (default 60 s): retry once; on second
failure: escalate. The watcher writes `dispatch-ack` or `dispatch-failed` to the
sender's inbox automatically. See `docs/dispatch-protocol.md` §3.

**Rule 4 — Never dispatch a sprint cross-tier.**
Sprint dispatch routes ONLY through the **Epic Lead's team system**
(`SendMessage` + `TaskUpdate`). A Tier-1 role writing directly to a sprint session's
inbox bypasses the Epic Lead and is a protocol violation. See `docs/dispatch-protocol.md`
§5.

### Tier 2 — Sprint Agent (team task system, OR file signals when standalone)

**Who:** sprint-agent. Two spawn shapes with different signaling:

- **Three-tier (team member):** spawned by an Epic Lead via `TeamCreate`/`Agent`.
  Communicates **primarily via the team task system** (`SendMessage` + `TaskUpdate`),
  not file signals — with two exceptions: it writes a `ready-to-merge.signal` file
  (alongside SendMessage), and it may fall back to a file signal for skip-tier
  escalation when the Epic Lead is offline (see `docs/agent-team-protocol.md`).
- **Two-tier (standalone):** spawned directly by a Phase Lead via `/start-sprint` as a
  named `--remote-control` session — there is **no Epic Lead team to message**. It
  signals its Phase Lead using the **Tier 1 file-signal mechanism** (`.claude/bin/signal-parent.sh`
  upward; status-board signal files), exactly like a lead session. Do not rely on
  `SendMessage` in this shape — there is no team mailbox.

For the three-tier team-member shape, normal coordination is:

**Upward (to Epic Lead):**
```
SendMessage(to: "<epic-lead-session>", message: "<body>")
```
Write a `ready-to-merge.signal` file AND SendMessage when a sprint PR is ready.
Use `TaskUpdate` to keep task state current.

**Downward:** Sprint Agent may spawn Task Subagents via the `Agent` tool. No
explicit downward signaling is needed — the Agent tool call is synchronous.

**Never idle:** if blocked, SendMessage to Epic Lead immediately. Do not wait for
a signal file to be noticed.

### Tier 3 — Task Subagent (result return)

**Who:** task-subagent, spawned by Sprint Agent via the `Agent` tool

Task Subagent has no active signaling mechanism. It communicates by:

1. **Completing its task** with a thorough, self-contained result.
2. **Terminating cleanly** so the result is returned to the Sprint Agent.

"Signalling upward" for a Task Subagent means producing a comprehensive answer and
stopping. Do not attempt to write signal files or call SendMessage — those are not
in scope and the tools may not be available.

**Never idle:** if you cannot complete the task as given, say so explicitly in your
result and terminate. Do not spin.

## Spawn layout protocol (iTerm2 only — gated)

**This applies only when `extensions.iterm2_layout: true`** (iTerm2/macOS). With the flag
off — Windows, Linux, headless, tmux-only — ignore this section; spawns use the plain
`claude --agent …` command and layout/profile is manual. The role system itself does not
depend on it.

When the gate is on, spawn each subordinate with the fixed layout so the visual tree
mirrors the org tree (`docs/iterm2-layout-convention.md`):

| Spawner → spawned | Layout |
|---|---|
| Operator → VP apex (manual; the only manual spawn) | vertical split |
| VP of Engineering → Tech Lead + Release Manager + DevOps (native teammates) | vertical split |
| VP of Engineering → Phase Lead | new tab |
| Phase Lead → Epic Lead | vertical split |
| Epic Lead → Sprint Agent | horizontal split (teammate-mode runtime) |

Use `.claude/bin/spawn-role-iterm2.sh --layout <tab|vsplit|hsplit> <role> <session> <prompt-file>`
— it creates the pane already themed with the role's profile, and attaches it
**to the pane you run the helper from** (via `$ITERM_SESSION_ID`), not to whatever
window has focus. Pass `--session <iterm2-session-id>` to target a specific pane.

### Always spawn role sessions through the helper

**Never** bring up a role session with a raw `iterm2_split_pane` (or `osascript` split) followed by `claude --agent …` typed into the pane. That path inherits the **default iTerm2 profile** (e.g. "Vibe Coder") instead of `claude-<role>`, defeating the colour-coding. **Always** use `.claude/bin/spawn-role-iterm2.sh`, which creates the pane *with* the role profile. On `iterm2_layout` projects a `PreToolUse` guard (`.claude/hooks/block-raw-role-spawn.sh`) **mechanically blocks** a raw `claude --agent` spawn — if you hit that block, you raw-spawned; use the helper.

This applies to **every** role-session spawn — including one-off **verification / test / ad-hoc** spawns, not only the orchestration dispatch flow (DevOps→leads, VP→Phase Lead, etc.). The "I'm just quickly checking something" spawn is exactly where the raw-split habit slips back in.

(When `iterm2_layout` is off / non-iTerm2, there is no helper and no profile to get wrong — spawn with the plain `claude --agent …` command. The rule above is specifically about not hand-rolling an iTerm2 split when the helper exists.)

## Session name resolution

Resolve aliases to canonical session names before signaling:
- "Signal the PM" → `<proj-abbrev>-vp`
- "The CTO asked" → `<proj-abbrev>-tl`
- "Notify the RM" → `<proj-abbrev>-rm`
- "Phase P3" → `P3/<phase-slug>`
- "Epic E2" → `P<N>/E2/<epic-slug>`

Read `<project>` from `.claude/orchestration.yaml` → `project_name`, or from
your own session name prefix.
