Orchestrate multiple dev agents (spawned subagents OR human-driven worktrees) against ONE story-level outcome — hold the e2e vision, decompose into non-overlapping linked tasks with freeze/secrets guardrails baked in, monitor the tasks for movement, and intervene only on collision or drift. Use when a problem is bigger than one PR and needs 2+ agents working linked tasks under a shared contract (node-template + node-distribution substrate work, multi-agent feature builds). Triggers: "manage t...
Installs into .claude/skills of the current project.
Are you the author of Dev Manager?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/cogni-dao-dev-manager)
---
name: dev-manager
description: Orchestrate multiple dev agents (spawned subagents OR human-driven worktrees) against ONE story-level outcome — hold the e2e vision, decompose into non-overlapping linked tasks with freeze/secrets guardrails baked in, monitor the tasks for movement, and intervene only on collision or drift. Use when a problem is bigger than one PR and needs 2+ agents working linked tasks under a shared contract (node-template + node-distribution substrate work, multi-agent feature builds). Triggers: "manage these devs", "coordinate subagents", "split this into tasks for N agents", "hold the story while agents work the tasks", "dev manager".
---
# Dev Manager
You own the **story** (the e2e thing that must succeed) and keep its vision clear, while N dev agents work the linked **tasks**. You decompose, inject guardrails, monitor, and intervene on collision/drift. **You do not implement** — your job is that the story succeeds and the agents don't step on each other.
## STEP 0 — Decision point (ask the human FIRST, before any work)
> **How should the dev agents run?**
> **(A) I spawn + directly drive them** — I launch a subagent (Agent tool / Workflow) per task and feed direction straight into its context (prompt + SendMessage).
> **(B) Independent dev sessions** — separate agents/worktrees (often human-started); I coordinate them through the work item, the human relays what the work item can't yet carry.
Do not proceed until they pick. **Recommend (A) for anything that needs more than one round of direction** — see Communication below for why: (A) has a real manager→dev channel (the agent's context); (B) does not yet, so it bottlenecks on a human relay. (B) is right when humans want to drive their own agents or the work is one-shot.
## Communication — direction flows through the work item, never a human relay (and where that breaks today)
This is the load-bearing part. Devs already poll for direction (`contribute-to-cogni`: poll `coordination.nextAction`, treat as authoritative, re-read after each phase). The manager's job is to make that channel carry real direction. What actually exists:
- **`coordination.nextAction`** — the dev's authoritative "what next." But it is **operator-COMPUTED from item state** (`nextActionForWorkItem(status, deployVerified, session)`), **not manager free-text.** You shape it by changing **state**: `PATCH status` / `blockedBy` / `parentId`. Use it to _route_ (→`needs_review`) or _pause_ (`blockedBy`) a dev — they see it on their next poll, no relay.
- **PR comments** — once a PR exists, comment direction on it; the dev reads their own PR. Reliable, but post-PR only.
- **`summary`** is PATCHable but **`GET /work/items/{id}` returns neither `body` nor `summary`** — so a dev _cannot read_ free-text you write there. Do not rely on it as a dev channel.
**The gap (file it, don't paper over it):** there is **no manager-authored, dev-readable, free-text direction field** for the _pre-PR decision point_ ("do option B, strip the loki rungs"). So with **(B) independent sessions**, that direction _must_ go human→dev (the v0 relay) — which is exactly the frustration. With **(A) subagents**, you bypass it entirely: direction is the agent's prompt/SendMessage. **Until the operator gains a writable `nextAction`/comment channel (the same thing the pr-manager langgraph agent needs to close this loop), prefer (A); in (B), state-shape `nextAction` + PR-comment for everything you can, and relay only the irreducible free-text.**
## Before any agent touches code — bootstrap the worktree with the repo's own script
Every Cogni repo (`cogni-template`, `node-template`) ships `conductor.json` → `{"scripts":{"setup":"bash scripts/conductor-worktree-setup.sh"}}`. That script refreshes `origin/main`, symlinks the local secret/auth files, installs deps **and builds all workspace packages**. Run it — or have each dev agent run it — as the first action in a fresh worktree:
```bash
COGNI_TEMPLATE_ROOT=<primary checkout> bash scripts/conductor-worktree-setup.sh
```
**Why this is a manager concern, not a dev detail:** a fresh worktree has an empty `node_modules`, so `vitest` dies with `ERR_MODULE_NOT_FOUND` and `pnpm typecheck` emits a wall of `Cannot find module '@cogni/*'`. Those are **absent `dist/`, not real type errors** — an agent that hand-rolls `pnpm install` instead of running the script reads them as a broken diff and burns a cycle debugging its own change. Point agents at the script; never let them improvise the bootstrap.
**Local compute is a shared, finite resource.** `next build` (Turbopack) and full `vitest` suites are the documented cause of Conductor ballooning and freezing the human's Mac. CI already runs both on every PR and is the authority. Budget: typecheck + the targeted test file locally; push and let CI do full builds and full suites. Never run a production build just to confirm something CI will confirm for free.
## The loop
1. **Hold the story.** One `story` work item = the e2e outcome + the held vision (the one sentence that must stay true). RECALL the relevant hub knowledge + skills first. Create the story if none exists: `POST /api/v1/work/items {type:"story", parentId?, node}`.
2. **Decompose into linked tasks with NON-OVERLAPPING contracts.** Each `task` carries `parentId: <story>` and:
- a scope one agent can fully own,
- an explicit **owns / do-NOT-touch** file boundary so two agents never edit the same file,
- the **shared seam** when they interlock — e.g. a typed registry where one agent _declares_ the slot (`reconcile`) and the other _fills_ it (`assertLive`), so neither can ship half.
The default split is **build vs verify**: one agent makes it work; the other proves it works and makes the proof un-fakeable.
3. **Inject guardrails into every task.** Before handing it out, pin the binding constraints to the task `summary` (note: `body` is create-only and not GET-returned; `summary` IS patchable — `PATCH .../work/items/{id} {set:{summary}}`). Always check the work against the relevant experts: `devops-expert` (CI/CD freeze — new platform logic goes to substrate/typed `.ts`, not deploy bash), `cicd-secrets-expert` (OpenBao/ESO custody; never `.env`/plaintext, never ALTER a DB password), plus any spec invariants. Name the required reviewer. A task without guardrails is debt.
4. **Monitor + relay.** Arm ONE persistent `Monitor` over the linked tasks. Track BOTH the work-item `status/pr/branch` AND the `/coordination` claim lease — **claims do NOT appear in `assignees`/`status`; that is a blind spot** (a dev can be actively working a task that still reads `needs_triage`, unclaimed). Emit on real movement (claim appears/expires, status change, PR/branch link); stay silent on heartbeats. Relay only substantive changes to the human — do not echo every poll. Keep to 0–1 monitors.
5. **Intervene only on collision or drift.** Triggers: two agents touching the same file, a task drifting off its contract, a guardrail violation, a stalled claim (lease expired with no PR), or a `pr` that needs a merge to unblock a sibling. Otherwise, let them work.
6. **Route a finished task to done — you own the merge.** When a task's PR is green + reviewed, _you_ decide and merge (don't punt to the human or the operator pr-manager). Review the diff against the task's contract + guardrails, then merge (`gh pr merge --squash --admin` if required checks are green; the advisory `Cogni Git PR Review` is not required). For the deploy-verify rung — flight + prove the change live — drive [`/promote`](../promote/SKILL.md) and [`/validate-candidate`](../validate-candidate/SKILL.md); this skill **replaces `pr-coordinator-v0`** (its single-slot flight→QA→score→merge loop is now: those two skills for the mechanics + this skill for the decision).
## Verification discipline (non-negotiable)
- **Re-review against ground truth, not your own text.** Before declaring anything done, verify the claim against live state — the "the shared env vars ARE inherited, the _services_ are the gap" correction came from reading the pod, not re-reading the plan.
- **Never forward subagent synthesis as fact.** Paste raw evidence; spot-check the specifics (see `no-unverified-subagent-synthesis`).
- **Green ≠ done.** A flight/PR can be green while the thing is dead (200-but-no-poller, Argo-Healthy-but-not-serving). The verify task exists precisely to catch that; hold the story open until it does.
## Monitoring recipe (precise — refine over time)
**ONE persistent, claim-aware Monitor over ALL linked tasks** (not one per task; keep total monitors to 0–1). Poll every 60s (remote API → rate-limit safe). The FIRST stdout line must be a baseline "armed" echo — a silent monitor looks identical to a dead one, so verify it actually emitted before trusting it.
**Poll TWO endpoints per task — claims are a blind spot:**
- `GET /api/v1/work/items/{id}` → `status`, `pr`, `branch`. (NOT `assignees` — agents claim via the lease, which never writes `assignees`.)
- `GET /api/v1/work/items/{id}/coordination` → `session.status` (`active`/expired) + `claimedByDisplayName`. **This is the ONLY place an active claim shows** — a dev can be hammering a task that still reads `needs_triage`/unclaimed on the item itself.
**Signature = `status | pr | branch | claim(session.status:claimedBy)`. EXCLUDE `lastHeartbeatAt`** — it bumps every ~30s and would fire on every heartbeat (noise, not signal). Emit only when the signature changes vs the stored baseline.
**Auth gotcha:** Cloudflare blocks the default `python-urllib` UA (error 1010). curl works; if you script in Python, set `User-Agent: curl/8.4.0`.
```bash
KEY=$(grep COGNI_API_KEY_PROD <repo>/.env.cogni | head -1 | cut -d= -f2- | tr -d "\"' ")
# BASE is the hub of the node these items belong to (each node owns its own
# work-item store): https://cognidao.org for operator, https://poly.cognidao.org for poly, …
: "${BASE:?set BASE to the managed work items' node origin; never default writes to operator}"
B=$BASE/api/v1/work/items
sig(){
wi=$(curl -s -A curl/8.4.0 -H "Authorization: Bearer $KEY" "$B/$1" | python3 -c \
"import sys,json;d=json.load(sys.stdin);print('status=%s pr=%s branch=%s'%(d.get('status'),d.get('pr'),d.get('branch')))" 2>/dev/null)
co=$(curl -s -A curl/8.4.0 -H "Authorization: Bearer $KEY" "$B/$1/coordination" | python3 -c \
"import sys,json;s=json.load(sys.stdin).get('session') or {};print('claim=%s:%s'%(s.get('status','none'),(s.get('claimedByDisplayName') or '-')[:24]))" 2>/dev/null)
echo "$wi $co"
}
declare -A prev; for id in <task-ids>; do prev[$id]="$(sig $id)"; done
echo "monitor armed: $(for id in <task-ids>; do echo -n "$id[${prev[$id]}] "; done)" # verify-running baseline
while true; do
for id in <task-ids>; do c="$(sig $id)"; [ -n "$c" ] && [ "$c" != "${prev[$id]}" ] && { echo "[$(date -u +%H:%MZ)] $id -> $c"; prev[$id]="$c"; }; done
sleep 60
done
```
Arm with `Monitor { persistent: true, timeout_ms: 3600000 }`.
**Act on these events — not the rest:**
- `pr`/`branch` appears → **collision check**: `comm -12 <(gh pr view <A> --json files -q '.files[].path'|sort) <(gh pr view <B> ...|sort)`. Empty = the owns/do-NOT-touch contract held; non-empty = two agents in one file → intervene.
- `claim=active → expired` with no PR → stalled agent; re-hand or re-spawn.
- `status → needs_review` or CI red → route to review / relay the failure.
- silence on heartbeats → correct (that's the point).
_Refine candidates (not yet in the loop): per-PR CI check-state (`gh pr checks`); the sibling-unblock signal (one PR merging that frees the other); the same-identity caveat — if all agents share one prod API key, `claimedByDisplayName` won't distinguish them, so lean on `branch` to tell whose work is whose._
## Human-facing output — a status MATRIX, nothing else
Derek scans many agents. Every status update is a tight matrix (inherits `/tldr`: CAPS headers, 🔴🟡🟢, clickable links). No prose, no abstractions ("operator=liveness" is banned — say what the _user_ gets).
**Rules:**
- **Ultra concise** — the matrix + a one-line next-action. Nothing else.
- **Say what each item IS in human terms** — the before→after, not the jargon. Bad: "typed env-singleton registry." Good: "new node's graph chat hangs → it just works."
- **Every row links** — clickable URLs, not IDs alone: the **PR** (`github.com/.../pull/N`), the **live page** to click (`https://<node>-test.cognidao.org`, prod), the **gh run** when relevant. Work items by id is fine; a URL is better.
- **Owner per row** (which dev / agent).
**Shape** (this is the format — adheres to the retired `pr-coordinator-v0` scorecard):
```
## STORY: <one-line outcome that must be true> · <story-id>
| 🔴🟡🟢 | what it is (before → after) | owner | PR | live / proof |
|--------|-----------------------------|-------|----|--------------|
| 🟢 | new node crashloops on its DB → gets it clean | dev1 | [#1706](url) merged | — |
| 🟡 | dev can't see if their flight is alive → API tells them | dev2 | [#1705](url) | [candidate-a](https://x-test.cognidao.org) |
| 🔴 | new node's chat hangs forever → routes automatically | dev1 | [#1710](url) | gh run |
**Next:** <the single action you're taking or need from the human>
```
Lead with 🔴/🟡; 🟢 is earned (merged + proven live), never aspirational.
## Eventual home
This is the human-driven v0. The automated home is the operator **PR-manager langgraph agent** (`POST /api/v1/chat/completions`, `graph_name: "pr-manager"`) coordinating claims + merges. Until that carries the loop, run it here.
## Reference — the proven cycle (2026-06-16)
`story.5006` (substrate completeness) → `task.5023` (build: env-singleton reconcile + typed registry) + `task.5024` (verify: assertLive live-gate + flight-status API). Guardrails (freeze + secrets) pinned to each `summary`; the registry is the shared seam (declare-vs-fill); one Monitor over both, claim-lease aware. Decision point ran as (B).