name: develop description: Chorus Development workflow — claim tasks, report work, and spawn sub-agent workers for parallel execution. (Codex port) license: AGPL-3.0 metadata: author: chorus version: "0.10.0" category: project-management mcp_server: chorus
Develop Skill
This skill covers the Development stage of the AI-DLC workflow: claiming Tasks, writing code, reporting progress, submitting for verification, and managing sessions for sub-agent observability.
Overview
Developer Agents take Tasks created by PM Agents (via /proposal) and turn them into working code. Each task follows:
claim --> in_progress --> report work --> self-check AC --> submit for verify --> Admin /review
For multi-agent parallel execution, the main agent uses Codex's spawn_agent tool to launch worker sub-agents. Sessions are optional in the Codex port — multi-agent observability requires the main agent to create sessions manually and pass sessionUuid to workers.
Tools
Task Lifecycle:
| Tool | Purpose |
|---|---|
chorus_claim_task |
Claim an open task (open -> assigned) |
chorus_release_task |
Release a claimed task (assigned -> open) |
chorus_update_task |
Update task status (in_progress / to_verify) |
chorus_submit_for_verify |
Submit task for admin verification with summary |
Work Reporting:
| Tool | Purpose |
|---|---|
chorus_report_work |
Report progress or completion (writes comment + records activity, with optional status update) |
Acceptance Criteria:
| Tool | Purpose |
|---|---|
chorus_report_criteria_self_check |
Report self-check results (passed/failed + optional evidence) on structured acceptance criteria |
Session (optional, main agent manages):
| Tool | Purpose |
|---|---|
chorus_session_checkin_task |
Checkin to a task before starting work |
chorus_session_checkout_task |
Checkout from a task when work is done |
Main agent (when coordinating workers for observability): call chorus_create_session before spawning workers, pass the returned sessionUuid to the worker via spawn_agent message, and call chorus_session_checkout_task + chorus_close_session after the worker finishes.
Workers: receive sessionUuid in their initial prompt and pass it to chorus_update_task and chorus_report_work for attribution.
No session needed: everything still works — task status, reports, comments — you just lose per-worker attribution in the UI.
Shared tools (checkin, query, comment, search, notifications): see /chorus
Workflow
Step 1: Check In
chorus_checkin()
Review your persona, current assignments, and pending work counts.
Step 1.5: Get Your Session (Codex port: main agent explicit)
Codex port does not auto-create sessions. Two scenarios:
- Single-agent work (main agent): skip session entirely. Call task tools without
sessionUuid. - Multi-agent work (worker spawned via
spawn_agent): the main agent should have created a session and passedsessionUuidin your initial prompt. Use it for everychorus_update_task,chorus_report_work,chorus_session_checkin_task,chorus_session_checkout_taskcall. If the main agent forgot to pass one and you still need observability, you MAY callchorus_create_sessionyourself — but coordinate with the main agent to avoid duplicates.
Step 2: Find Work
chorus_get_available_tasks({ projectUuid: "<project-uuid>" })
Or check existing assignments:
chorus_get_my_assignments()
Step 3: Claim a Task
chorus_get_task({ taskUuid: "<task-uuid>" }) # Review first
chorus_claim_task({ taskUuid: "<task-uuid>" })
Check: description, acceptance criteria, priority, story points, related proposal/documents.
Step 4: Gather Context
Each task and proposal includes a commentCount field — use it to decide which entities have discussions worth reading.
Read the task and identify dependencies:
chorus_get_task({ taskUuid: "<task-uuid>" })Pay attention to
dependsOn(upstream tasks) andcommentCount.Read task comments (contains previous work reports, progress, feedback):
chorus_get_comments({ targetType: "task", targetUuid: "<task-uuid>" })Review upstream dependency tasks — your work likely builds on theirs:
chorus_get_task({ taskUuid: "<dependency-task-uuid>" }) chorus_get_comments({ targetType: "task", targetUuid: "<dependency-task-uuid>" })Look for: files created, API contracts, interfaces, trade-offs.
Read the originating proposal for design intent:
chorus_get_proposal({ proposalUuid: "<proposal-uuid>", section: "documents" })(
chorus_get_proposaldefaults tosection: "basic"— just metadata + a draft index. Passsection: "documents"for the design docs, orsection: "full"for docs + task drafts.)Read project documents (PRD, tech design, ADR):
chorus_get_documents({ projectUuid: "<project-uuid>" })
Document update flow (OpenSpec mode): if the originating proposal
descriptioncontains a lineOpenSpec change slug: <slug>, the project's PRD / tech_design / spec Documents are mirrors of files underopenspec/changes/<slug>/. To update such a Document (e.g. clarify an AC, fix a spec scenario before resubmitting), load theopenspec-awareskill at~/.codex/skills/openspec-aware/SKILL.mdand follow §3.8: edit the local.mdfile first, then mirror through thechorus-mcp-call.shwrapper withjson_encode_fileandchorus_check_response.⛔ Do not call
chorus_pm_update_documentdirectly from Codex's MCP harness with a hand-typedcontentfield in OpenSpec mode. The local file is the source of truth; agent-typed content drifts and burns tokens (openspec-aware§2 Rule 1).When the LAST task of an OpenSpec idea is verified, the plugin's PostToolUse hook injects an archive reminder (
openspec-aware§3.9) — runopenspec archive <slug> --yes, then mirror each emittedopenspec/specs/<capability>/spec.mdback via §3.8.In the no-OpenSpec fallback (no slug line, or no
openspecCLI), edit the Document content directly via the existing MCP tool with no wrapper, no local file step.
Step 5: Start Working
With session (optional): checkin to the task first, then mark in-progress:
chorus_session_checkin_task({ sessionUuid: "<session-uuid>", taskUuid: "<task-uuid>" })
chorus_update_task({ taskUuid: "<task-uuid>", status: "in_progress", sessionUuid: "<session-uuid>" })
Without session (single-agent / main agent):
chorus_update_task({ taskUuid: "<task-uuid>", status: "in_progress" })
Dependency enforcement: If this task has unresolved dependencies (dependsOn tasks not in
doneorclosed), the call will be rejected with detailed blocker info. Usechorus_get_unblocked_tasksto find tasks you can start now.
Step 6: Report Progress
Report periodically with chorus_report_work. Include:
- What was completed
- Files created or modified
- Git commits and PRs
- Current status / remaining work
- Blockers or questions
chorus_report_work({
taskUuid: "<task-uuid>",
report: "Progress:\n- Created src/services/auth.service.ts\n- Commit: abc1234\n- Remaining: unit tests",
sessionUuid: "<session-uuid>"
})
Report with status update when complete:
chorus_report_work({
taskUuid: "<task-uuid>",
report: "All implementation complete:\n- Files: ...\n- PR: https://github.com/org/repo/pull/42\n- All tests passing",
status: "to_verify",
sessionUuid: "<session-uuid>"
})
Step 7: Self-Check Acceptance Criteria
Before submitting, check structured acceptance criteria:
task = chorus_get_task({ taskUuid: "<task-uuid>" })
# If task.acceptanceCriteriaItems is non-empty:
chorus_report_criteria_self_check({
taskUuid: "<task-uuid>",
criteria: [
{ uuid: "<criterion-uuid>", devStatus: "passed", devEvidence: "Unit tests cover this" },
{ uuid: "<criterion-uuid>", devStatus: "passed", devEvidence: "Verified manually" }
]
})
For required criteria, keep working until you can self-check as
passed. Only usefailedfor optional criteria that are out of scope.
Step 8: Submit for Verification
Sub-agents — checkout first:
chorus_session_checkout_task({ sessionUuid: "<session-uuid>", taskUuid: "<task-uuid>" })
Then submit:
chorus_submit_for_verify({
taskUuid: "<task-uuid>",
summary: "Implemented auth feature:\n- Added login/logout endpoints\n- JWT middleware\n- 95% test coverage\n- All AC self-checked (3/3 passed)"
})
to_verifydoes NOT unblock downstream tasks — onlydone(after admin verification) does.
Review Agent: After
chorus_submit_for_verify, the Chorus plugin's PostToolUse hook injects context instructing you to spawn thechorus-task-reviewersub-agent. You MUST spawn it yourself (it is NOT auto-launched). Spawn it by mounting this plugin'schorus-task-reviewerskill into a default sub-agent:spawn_agent( agent_type="default", items=[ { type: "skill", name: "Chorus Task Reviewer", path: "chorus:chorus-task-reviewer" }, { type: "text", text: "Review Chorus task <task-uuid>. Post VERDICT comment." } ] ) wait_agent([reviewer_id]); close_agent(reviewer_id)Why not
agent_type="chorus-task-reviewer"? Codex 0.125 only has three built-in roles (default / explorer / worker); custom review personas are loaded by mounting the skill. The reviewer posts aVERDICT:comment on the task.
After the reviewer completes, read its VERDICT:
chorus_get_comments({ targetType: "task", targetUuid: "<task-uuid>" })
Find the most recent comment containing VERDICT: and act on it:
- VERDICT: PASS — All AC verified, no issues. Proceed to admin verification.
- VERDICT: PASS WITH NOTES — All AC verified, minor notes. Proceed to admin verification (notes are non-blocking).
- VERDICT: FAIL — BLOCKERs found. Do NOT verify. Fix the BLOCKERs listed in the reviewer's comment, then resubmit.
If no new VERDICT: comment appears after the reviewer returns, it exhausted its maxTurns budget before posting. Respawn it ONCE with a concise-budget hint in the prompt: "Stay within turn budget. Skip deep verification. Fetch task/proposal/comments, run only the core tests, and post your VERDICT comment within the first 12 turns." If the second attempt still produces no VERDICT, review manually using the checklist and proceed.
Step 9: Handle Review Feedback
If the reviewer returns FAIL, or the task is reopened after verification:
All acceptance criteria are reset to pending when a task is reopened.
- Check feedback:
chorus_get_task({ taskUuid: "<task-uuid>" }) chorus_get_comments({ targetType: "task", targetUuid: "<task-uuid>" }) - Fix every BLOCKER listed in the reviewer's FAIL comment.
- Checkin again, fix issues, report fixes, resubmit.
Step 10: Task Complete
Once Admin verifies (status: done), move to the next available task (back to Step 2).
Step 11: Idea Completion Report (advisory)
If the task you just self-verified was the LAST one of its Idea (every Task across every approved Proposal is now done/closed) and you have document:write, prompt the user and call chorus_create_report on accept. The tool description carries the section template. Skip on decline — the PostToolUse hook will remind on the next run.
Session (Optional, Codex Port)
The Codex port is currently stateless — no hook auto-creates, heartbeats, or closes Chorus sessions yet. Codex supports SubagentStart/SubagentStop plugin hooks, but this plugin has not wired them into automatic session lifecycle management. Treat sessionUuid as optional per-worker observability, not a requirement:
- Single-agent developer work: skip session tools entirely.
chorus_update_task/chorus_report_work/chorus_submit_for_verifyall work without asessionUuid. - Team Lead orchestrating workers via
spawn_agent: manually callchorus_create_sessionbefore spawning, passsessionUuidinto each worker's initial message, andchorus_close_sessionafterwait_agentreturns. See the Multi-Agent Workers section below.
Multi-Agent Workers (Codex spawn_agent)
When running multiple sub-agents in parallel on a proposal's tasks, the main agent plays Team Lead. The Codex port does not auto-manage sessions — the Team Lead is responsible for session lifecycle if per-worker observability is needed.
Two-Layer Architecture
| Layer | System | Purpose |
|---|---|---|
| Orchestration | Codex spawn_agent |
Spawning sub-agents, passing task assignments |
| Work Tracking | Chorus MCP | Task lifecycle, work reports, (optional) session observability |
Team Lead Workflow (with sessions)
# 1. Check in and plan
chorus_checkin()
chorus_list_tasks({ projectUuid: "<project-uuid>" })
chorus_get_unblocked_tasks({ projectUuid: "<project-uuid>" })
# 2. For each worker you intend to spawn, create a Chorus session
session_a = chorus_create_session({ name: "frontend-worker" })
session_b = chorus_create_session({ name: "backend-worker" })
# 3. Spawn workers, pass sessionUuid + taskUuid(s) in the message
spawn_agent(
agent_type="worker",
message=f'''You are a Chorus developer worker. Follow the $develop skill.
Your sessionUuid: {session_a.uuid}
Your task(s): <task-uuid-1>, <task-uuid-2>
Project UUID: <project-uuid>
Procedure: for each task — chorus_session_checkin_task → chorus_update_task in_progress → implement → chorus_report_work → self-check AC → chorus_session_checkout_task → chorus_submit_for_verify. Report completion in your final message so the main agent can close your session.''',
)
Team Lead Workflow (without sessions — simpler)
If per-worker observability is not required, skip sessions entirely:
spawn_agent(
agent_type="worker",
message='''Follow $develop skill. Your task: <task-uuid>. Do NOT call chorus_create_session or chorus_session_*. Just claim/update/report/submit.''',
)
Task status, work reports, comments, AC self-checks all still function — you only lose "which worker did what" attribution in the UI.
Session Cleanup (Team Lead responsibility)
Until the Codex plugin wires SubagentStop into Chorus session cleanup, the Team Lead must close sessions after workers finish:
# After worker_a's spawn_agent returns:
chorus_session_checkout_task({ sessionUuid: session_a.uuid, taskUuid: "..." }) # if worker forgot
chorus_close_session({ sessionUuid: session_a.uuid })
Alternatively, rely on Chorus backend session TTL to auto-expire idle sessions. This is acceptable for most cases.
Wave-Based Execution
Server-side enforcement:
chorus_update_task(status: "in_progress")rejects if anydependsOntask is notdoneorclosed.
chorus_get_unblocked_tasks— find ready tasks- Spawn workers for Wave 1
- After each worker returns, verify its task (
chorus_admin_verify_task→done) chorus_get_unblocked_tasksagain — find newly unblocked tasks (Wave 2)- Repeat until all tasks done
Critical:
to_verifydoes NOT resolve dependencies — onlydoneorcloseddoes. The Team Lead must verify tasks between waves.
Multiple Tasks Per Worker
A single worker can work on multiple tasks sequentially — write them in its spawn_agent message in dependency order, and have the worker loop over them.
MCP Access for Workers
Sub-agents spawned via spawn_agent inherit the parent's MCP configuration. Ensure the chorus MCP server is declared in ~/.codex/config.toml or the repo .codex/config.toml with CHORUS_URL / CHORUS_API_KEY set.
Troubleshooting
| Problem | Solution |
|---|---|
| Worker can't access Chorus MCP tools | Verify MCP is configured and CHORUS_API_KEY has task: ["write"] permission |
| UI doesn't show active worker | Worker forgot chorus_session_checkin_task, or main agent didn't create a session. Sessions are optional — it's fine to not have one |
| Session shows "inactive" (yellow) | No heartbeat — backend session TTL will clean it up, or call chorus_close_session explicitly |
| Task stuck in wrong status | Use chorus_update_task to reset status manually |
| Duplicate sessions | Main agent created session AND worker also called chorus_create_session. Pick one owner (prefer main agent) |
| Worker didn't close its session | Main agent calls chorus_close_session({sessionUuid}) after spawn_agent returns |
Work Report Best Practices
Good report (enables session continuity):
Implemented password reset flow:
Files created/modified:
- src/services/auth.service.ts (new)
- src/app/api/auth/reset/route.ts (new)
- tests/auth/reset.test.ts (new)
Git:
- Commit: a1b2c3d "feat: password reset flow"
- PR: https://github.com/org/repo/pull/15
Implementation details:
- POST /api/auth/reset-request: sends email with token
- Token expires after 1 hour, single-use
- Rate limiting: 3 requests/hour/email
- 12 new tests, all passing
Acceptance criteria:
- [x] User can request reset via email
- [x] Reset link expires after 1 hour
- [x] Rate limiting prevents abuse
Bad report: Done.
Tips
- Read task comments first — they contain previous work reports for session continuity
- Check upstream dependencies — read
dependsOntasks and their comments for interfaces/APIs - Read the originating proposal — understand design rationale and task DAG
- Use
commentCount— skip fetching comments on entities with count 0 - Report progress frequently — include file paths, commits, and PRs
- Write detailed submit summaries — Admin needs them to verify
- If blocked, add a comment and consider releasing the task
- One task at a time: finish or release before claiming another
- Use meaningful sub-agent names — they become Chorus session names
When to Release a Task
Release if:
- You can't complete it (missing knowledge, blocked)
- A higher-priority task needs attention
- You won't finish in a reasonable timeframe
chorus_release_task({ taskUuid: "<task-uuid>" })
chorus_add_comment({ targetType: "task", targetUuid: "<task-uuid>", content: "Releasing: reason..." })
Next
- After submitting for verification, an Admin reviews using
/review - For platform overview and shared tools, see
/chorus