Skip to content
Development
Skill

/team-protocol

Use whenever one role needs something from another role — a blocker, a spec question, a cross-platform divergence, a handoff, or an escalation. Defines the durable team message ledger, who may talk to whom, the anti-ping-pong guard, and when to escalate to the user instead.

From plugin
app-dev-team
432 skills30 agents27 commands2 hooks
Install
$ npx -y skills add vmobifystudio/app-dev-team --skill team-protocol --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/team-protocol

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use whenever one role needs something from another role — a blocker, a spec question, a cross-platform divergence, a handoff, or an escalation. Defines the durable team message ledger, who may talk to whom, the anti-ping-pong guard, and when to escalate to the user instead.

SKILL.md

team-protocol.SKILL.md
name: team-protocol
description: Use whenever one role needs something from another role — a blocker, a spec question, a cross-platform divergence, a handoff, or an escalation. Defines the durable team message ledger, who may talk to whom, the anti-ping-pong guard, and when to escalate to the user instead. Triggers from every agent that would otherwise say "I need X from Y" and stop.

Team protocol

A team is not a set of roles. It is a set of roles plus an agreed way to talk.

Without one, this happens: an IC hits an ambiguity, writes `BLOCKED: needs tech-lead`, and exits. The orchestrator reads it, spawns `tech-lead` with a paraphrase, gets an answer, paraphrases it back, and re-spawns the IC from cold. Three context rebuilds, two lossy translations, and the original question is now a summary of a summary.

The channel: `docs/team/messages.jsonl`

One append-only event log for the whole team, schema `studio-event-schema/v1`. It works on a vanilla install, survives an agent dying mid-run, and gives a restarted agent its history back.

`docs/team/messages.md` is the **generated human view** of that log — the same relationship `docs/31-board.md` has to `docs/31-board-events.jsonl`. **Never hand-edit it.** A hand edit is overwritten by the next render and is invisible to every rule.

{"id":"MSG-0421","v":1,"ts":"2026-07-29T10:12Z","project":"tipjar","thread":"THR-APP-004",
 "ticket":"APP-004","kind":"question","from":"android-developer","to":["tech-lead"],
 "priority":"material","blocking":false,"requires_response":true,"expires_after_round":6,
 "requirements":["REQ-031"],"summary":"Which error type for a failed toggle?",
 "body":"Spec names TodoError but the repo throws IOException. Which wins?","status":"open"}

**Kinds:** `question` · `answer` · `handoff` · `blocker` · `fyi` · `escalation` · `decision`

**`answer` and `decision` each close exactly one open `question` on that ticket** — pairing is by count, oldest first. So do not use `decision` for a note that decides nothing: it will silently consume a real open question. Observed live — a `decision` correcting a tooling mistake made a genuinely unanswered product question look resolved, and the ticket had already shipped on the assumption underneath it. **Use `fyi` for anything that is not an answer.**

Rules:

  • **Append, never edit.** Correct a wrong record by appending a later one.
  • **`--summary` is one line and must stand alone.** It is what the orchestrator and the user read.
  • **Every message names a ticket** — or the ASCII hyphen `-`, and only `-`, for project-wide

chatter: `--ticket -`. This said `—` (an em dash), which `team-message.sh` does not recognise as the sentinel: it treats it as an ordinary ticket ID, so every project-wide broadcast joined one pseudo-thread and the third one was **refused by the anti-ping-pong pair guard**. The guard is skipped for `-` precisely because broadcast chatter carries no thread. A message with no ticket at all cannot be routed or closed.

  • A `question` is not resolved until an `answer` **or** a `decision` on the same ticket exists.

`board-doctor` reads the channel (as a sibling of the board) and reports `question_unanswered` — and says explicitly when the ticket has already reached `qa`/`done`, i.e. shipped on an unconfirmed assumption.

Write with the helper. It is the only writer; there is no hand-edit fallback, because a row appended by hand routes around every rule below:

sh "${CLAUDE_PLUGIN_ROOT}/scripts/team-message.sh" \
   --from android-developer --to tech-lead --ticket APP-004 --kind question \
   --summary "Which error type for a failed toggle?" \
   --body "Spec names TodoError but the repo throws IOException. Which wins?"

**A project that predates the event log keeps working.** The first send migrates `docs/team/messages.md` into `docs/team/messages.jsonl`, announces that it did, and marks every migrated record `provenance:"inferred"` — priority, status, thread and the follow-up round were never recorded in Markdown, so they were reconstructed, not read. Nothing is stranded and nothing is claimed that was not there.

Message obligations — what a message must yield

Every **material** message must yield one of four things:

| Obligation | How you satisfy it | |---|---| | a decision | `--kind decision`, or `--decision "<the call>"` | | a state transition | `--transition APP-004:merged` | | an artifact update | `--artifact ADR-003` or `--artifact docs/22-impl-spec-ios.md` | | a timed follow-up | automatic on `question`/`blocker`/`escalation`/`handoff`; set explicitly with `--expires-after-round N` |

A message with none of them is **refused at send time, with the reason**. It never reaches the log.

The sharp edge: **an `answer` or a `decision` that names no artifact is refused.** A closed ledger is not delivery (DR4-006) — if the answer was not folded into a spec, an ADR, or a ticket transition, the next agent to read the spec still reads the old answer, and "every question answered" was the metric that hid it. `messages-render` has a `DELIVERY` block listing every one that slipped through.

**`fyi` is the escape hatch, and it must be chosen.** `--kind fyi` (or `--priority fyi`) exempts a message from the obligation rule. Nothing defaults into it: if you find yourself reaching for it to get a message past the check, the message probably should not be sent.

Threads and channels are derived, never authored

A **thread** is the messages sharing a ticket. A **channel** is a query over the log: `#founder-decisions` · `#product` · `#design` · per-platform (`#ios`, `#android`, `#backend`) · per-ticket (`#app-004`) · `#artifacts`. Nothing subscribes and nothing is filed into a channel — membership is computed from who sent it, to whom, about what.

node "${CLAUDE_PLUGIN_ROOT}/scripts/messages.mjs" channels     # every channel the log can produce

This is the board's rule again: a view may only show what the log c

Read more
Ships withapp-dev-team

Describe your app idea in one line. Get a shipped iOS & Android app. AI App Studio is a team of 30 AI specialists — a CEO, product manager, designers, iOS/Android engineers, a code reviewer, QA, and a release manager — that works like a real software studio.

Get the whole plugin

Other skills on app-dev-team.