Imported from neonwatty/agent-conveyor (
skills/manage-codex-workers/SKILL.md). Install upstream withnpx skills add neonwatty/agent-conveyor --skill manage-codex-workers. Copyright stays with the author.
Manage Codex Workers
Use conveyor ... as the primary CLI. It is installed by the agent-conveyor
Node/TypeScript package. The legacy workerctl command remains a compatibility
alias, but skill-driven flows should prefer conveyor.
One-Prompt Codex App Ralph Loop
This is the preferred entry point when the user has installed Agent Conveyor and wants to use another Codex app session without learning the low-level command sequence.
User prompt:
Use the manage-codex-workers skill.
Set up a Codex app Ralph loop for issue CTL.
Worker session: <worker-name or choose one>
Manager session: <manager-name or choose one>
Template: <template name, or choose the best one>
Max iterations: <number, default 3>
Require adversarial proof before another worker iteration.
For repo delivery where the user explicitly grants branch, PR, CI, conflict,
and merge authority, use the ship-it-loop recipe and ship_it_loop template
instead of a generic Ralph loop:
Use the manage-codex-workers skill.
Set up a Codex app autonomous ship-it loop for <bounded repo task>.
Manager may push a branch, open a PR, monitor CI, resolve bounded conflicts,
and merge only after explicit manager merge evidence.
Max iterations: <number, default 2>
For creative-ops work where the user wants one manager to supervise multiple Codex app workers across channels such as YouTube, TikTok, LinkedIn, Facebook, image generation, HyperFrames, or copy drafts, use the Creative Ops Campaign recipe. It should create campaign records, worker slots, channel briefs, slot-scoped assignments, asset receipts, and dashboard status proof. It must not imply public publishing or scheduling without explicit human approval.
Skill behavior:
- Work from
/Users/neonwatty/Desktop/codex-terminal-manager. - Run
conveyor doctorandconveyor db-doctor; fix or report blockers. - Choose concise task, worker, manager, and run names when the user does not provide them. Do not ask the user to invent generated names.
- Preferred fully app-native setup: if running in the Codex app and thread
tools are available, create fresh same-project manager and worker threads
with
create_thread, give both readable titles, and keep both returned thread ids/titles. Usecreate_threadfor this flow; do not usefork_threadunless the user explicitly asks to fork or resume this exact conversation. If the current thread is explicitly acting as the manager, use the current thread as manager and create only the worker thread. - Create the no-tmux binding with
conveyor create-disposable-binding. For same-thread Codex app visible-session loops, prefer--template app_visible_build_loopfor build/test dogfood or a custom--required-before-continue adversarial_checkgate; do not choose cleanup-gated templates such asbuild_then_clearorcompact_then_continueunless the setup creates a fresh worker thread per iteration or has a concrete app-session cleanup receipt path. For an operator-approved ship-it loop, use--template ship_it_loop, save theship-it-loopmanager recipe, and require branch, push, PR, CI, mergeability, manager merge decision, merge, post-merge, andadversarial_checkreceipts before merge or closeout. Use--adversarial, a bounded--max-iterations, and--json. When step 4 produced app thread ids, pass them through--manager-codex-app-thread-id,--manager-codex-app-thread-title,--worker-codex-app-thread-id, and--worker-codex-app-thread-titleso Conveyor can surface app identities insessions,discover, and setup JSON. If app thread tools are not available, create the binding without those flags and ask the user to open separate Codex app sessions manually. - Ensure Dispatch is running or tell the user the single command to start it:
conveyor dispatch --watch --dispatcher-id dispatch-local. - Read the returned
communicationblocks. A worker or manager withsession_kind=tmuxandreceive_style=pushcan receive direct tmux pushes; one withsession_kind=codex_appandreceive_style=pullmust poll the printedapp-heartbeatcommand, using direct inbox commands only when the heartbeat output or setup JSON provides them as fallbacks. - Give the worker Codex app session the generated
worker_handoffprompt. If step 4 created a fresh worker thread, usesend_message_to_threadonly to deliver that bootstrap prompt. Durable manager/worker communication still goes through Dispatch andworker-inbox/manager-inbox; direct app-thread messages are not Dispatch receipts. The worker should keep polling the exact command printed in theworker_handoffprompt through the bounded loop until no inbox item remains ormax_iterationsis reached. Consuming acontinue_iterationinbox item advances the Ralph-loop run's durablecurrent_iterationand writesralph_loop_iteration_advancedtelemetry. After completing or blocking on any consumed item, the worker must run the generatedconveyor enqueue-notify-manager ... --jsoncommand and then run the generated one-iterationconveyor dispatch --watch ... --jsoncommand before its final app-thread answer. If either command fails, treat that as the blocker; a direct app-thread final answer alone is not manager notification and is not task completion. - After each worker pass, require concrete evidence and structured
loop-evidence adversarial-checkproof before queueing anotherenqueue-continue-iteration. - Use
conveyor loop-status <task> --run <run> --jsonand telemetry/audit receipts before declaring the loop ready for manager review.
Idle polling rule for Codex app/no-tmux sessions:
- When a worker has
session_kind=codex_apporreceive_style=pull, its default idle/check-in command is the returnedapp-heartbeatworker command. Usecommunication.poll_commandor the generatedworker_handoffcommand as fallback/direct inbox polling. - When a manager has
session_kind=codex_apporreceive_style=pull, its default idle/check-in command is the returnedapp-heartbeatmanager command. Usecommunication.poll_commandas fallback/direct inbox polling. - The printed command may include a local
PATH=.../bin:$PATH conveyorprefix; preserve that prefix when giving the command to a Codex app thread. - Repeat the appropriate command whenever the session is idle, after finishing a received instruction, and before deciding there is nothing more to do. A timeout is not completion; it is only a quiet poll interval.
- Keep
conveyor dispatch --watch --dispatcher-id dispatch-localrunning so Dispatch can route new messages into those inboxes. - After a worker consumes and acts on a manager instruction, require a durable
conveyor enqueue-notify-manager <task> --message ... --jsonreceipt plus a one-iteration Dispatch run before treating the worker pass as reported. A direct Codex app final answer is local thread text; it does not notify the manager inbox or prove the manager saw the result. - The actual manager and worker sessions are the primary human-reviewable
transcript. SQLite, replay, and status commands are audit proof, not a
substitute for visible work. When a manager or worker consumes an inbox item,
it must print live session sections before acting and before final answer:
CONVEYOR POLL,CONVEYOR RECEIVED,WORK,CONVEYOR SEND, andDISPATCH. Idle polls may end with one shortCONVEYOR IDLEreceipt, but consumed work must never be handled silently or only summarized later. - Prefer
conveyor app-autopilot start <task> --jsonfrom the operator or manager session when setting up a Codex app/no-tmux pair for autonomous heartbeat management. It records a durable pair-level heartbeat policy and emits manager/worker Codex app heartbeat automation specs plus the bounded Dispatch watch command. Useconveyor app-autopilot status <task> --jsonto inspect the current policy, loop health, andplan.quiescence. Ifplan.quiescence.recommended_actionisstop_autopilot, the operator should recordconveyor app-autopilot stop <task> --jsonand pause/delete the Codex app heartbeat automations instead of sending another idle pulse. Use--quiet-after Nto tune the number of paired manager/worker idle heartbeats required before that recommendation. The CLI cannot create or delete Codex app automations by itself; apply the emitted automation specs through Codex app automation tools. - Use
conveyor app-loop-status <task> --jsonas the operator status surface, andconveyor app-wakeup-plan <task> --jsonto produce exact stale-thread wake prompts for Codex app automation orsend_message_to_thread. - Use
conveyor app-wakeup-dispatch <task> --jsonwhen the manager needs an auditable wake orchestration receipt. It prepares adapter-ready app-thread actions, reports skipped and blocked roles, and records telemetry; it does not send direct app-thread messages itself. - If
create-disposable-binding --jsonreturnedheartbeat_recommendations.wakeup_dispatch_commandandheartbeat_recommendations.delivery_receipt_commands, prefer those exact generated commands when waking app threads and recording sent/skipped/blocked outcomes. - In a Codex app manager with thread tools available, use the native
send_message_to_threadtool only forapp-wakeup-dispatchactions wheresend_ready=true. After each send, runconveyor app-wakeup-record-delivery <task> --role <role> --dispatch-receipt <receipt.event_id> --delivery-status sent --thread-id <action.thread.id> --json. Forskipped_healthyactions, record--delivery-status skipped; forblocked_missing_thread, record--delivery-status blocked. These delivery receipts audit app-thread wake attempts; they do not replace Dispatch inbox, heartbeat, or task evidence. - For Codex app/no-tmux worker cleanup, do not rely on sending
/compactor/clearwithsend_message_to_thread; that arrives as ordinary prompt text. Preferconveyor app-worker-rotation-plan <task> --old-worker-thread-id <active-worker-thread-id> --require-handoff --json. Only if the plan reportseligible=true, create the replacement worker thread from the emittedcreate_replacement_worker_thread.prompt, archive exactly the emittedarchive_old_worker_thread.thread.id, then run the emittedapp-worker-rotation-recordcommand with the new thread id/title. The manager must never archive a thread that is not the active bound worker in the current plan. - For bounded Ralph loops, treat
ralph_loop_iteration_advancedtelemetry as the receipt that a worker actually consumed and began the requested iteration.
Reference docs:
README.mdcommand referencedocs/qa/ralph-loop-operator-guide.mddocs/agent-evidence-playbook.md
Supervision Model
Supervision is built on three primitives: sessions, tasks, and bindings.
- A worker session is a Codex session registered with Agent Conveyor. It may be a
tmux-backed session or a Codex app/no-tmux session. Its rollout JSONL on disk
(
~/.codex/sessions/.../rollout-*.jsonl, or a disposable rollout file) is the source of truth for ingest. - A manager session is a Codex session that can run anywhere — Ghostty,
iTerm2, Terminal.app, a web terminal. The manager does not need tmux. Its
job is to call
conveyorcommands, read their JSON output, and decide what to do next. - Registration,
sessions,discover, and disposable binding JSON include acommunicationblock. Use it to decide the receive style for both worker and manager: tmux sessions are push-capable, while Codex app/no-tmux sessions receive throughmanager-inboxorworker-inboxpolling. - App-assisted setup may also record optional
codex_app_thread_idandcodex_app_thread_titlemetadata. This identifies the human-readable Codex app thread; it does not replace rollout ingest or Dispatch inbox receipts. - A task is a unit of supervised work with a goal.
- A binding ties one worker session and one manager session to one task.
The manager Codex drives the supervision loop by calling
conveyor cycle <task> repeatedly. Each cycle ingests new rollout events,
captures the worker's tmux pane as a shadow signal, persists a manager_cycles
row, and returns structured JSON. The manager reads that JSON and decides.
Dispatch is core infrastructure for supervised pairs. The pair workflow starts
a detached Dispatch watch process by default after worker/manager setup and
bind. For manually bound pairs, keep Dispatch running in another shell with:
conveyor dispatch --watch --dispatcher-id dispatch-local
Dispatch wakes the bound manager on worker completion and executes queued
notify_manager / nudge_worker commands. It does not decide whether the task
is correct or finished.
Preflight
- Work from the control repo:
cd /Users/neonwatty/Desktop/codex-terminal-manager - Verify dependencies:
conveyor doctor - Verify the SQLite control plane is healthy:
conveyor db-doctor - From the current Codex session, check whether it can register itself:
conveyor doctor-selfsupported: truemeans the session is inside a live tmux session and can be registered as a worker. A non-tmux session can still be registered as a manager.
Discovery For Q&A
When the user asks which worker, manager, task, or binding to connect, search the control plane first and present likely choices instead of asking for generated names:
conveyor discover <query>
conveyor search <query>
Use an empty query to list active candidates. Add --all only when the user is
looking for completed tasks or gone sessions. The JSON output includes
tasks, sessions, bindings, telemetry, and suggestions; use
suggestions to offer concise next steps such as a conveyor bind command or
the prompt to register a missing worker or manager.
Preferred Manual Handoff Workflow
When the user wants to hand off an already-open Codex session, do not start
with a long pair command. Use the skill in each session:
- In the intended worker session, ask Codex:
Use the manage-codex-workers skill. Register this current Codex session as the worker for this dashboard setup. Dashboard setup code: <setup-code> Working directory: <target-repo> Let the skill derive the task and session names from the setup code. Do not ask me to type generated worker, manager, or task names. After registration, wait for the manager. Do not start work until the manager has created or bound the task and provided acceptance criteria. - In a separate manager session, ask Codex:
Use the manage-codex-workers skill. Register this current Codex session as the manager for this dashboard setup. Dashboard setup code: <setup-code> Working directory: <target-repo> Goal: <goal> Let the skill derive the task and session names from the setup code, find the matching worker, create/configure the task if needed, and bind the worker and manager. Run cycles, inspect criteria and telemetry, nudge only when useful, require evidence, and finish/export the task when done. - The manager session should then drive the loop with:
Forconveyor cycle <task-name> conveyor criteria <task-name> --list conveyor telemetry --summary --task <task-name> conveyor telemetry --task <task-name> conveyor replay <task-name>pair-started workflows, Dispatch is started automatically unless--no-dispatchis passed. For manually bound pairs, keepconveyor dispatch --watch --dispatcher-id dispatch-localrunning in a separate shell while the pair is active, or run a bounded verification pass withconveyor dispatch --watch --watch-iterations 2 --dry-run --json.
The skill should translate those prompts into explicit conveyor commands.
For the worker, run doctor-self; if supported, register the current session
with register-worker. For the manager, register the current session with
register-manager, create/configure the task if needed, then bind.
When the prompt includes a dashboard setup code, derive names without asking the user:
task: dashboard-<setup-code>
worker: dashboard-<setup-code>-worker
manager: dashboard-<setup-code>-manager
If there is already a registered worker for the derived worker name, reuse it when binding the manager. If the derived name collides with an active unrelated session, append a short suffix yourself and continue; do not ask the user to invent names.
If the prompt has no setup code and no explicit names, choose concise names from the task goal or current date yourself. Ask the user only when the target repo or goal is missing or ambiguous.
This is the ergonomic manual workflow. Use pair only when the user wants
conveyor to spawn both sessions in one automated command.
Register Sessions
Register an already-running Codex worker (rollout JSONL is auto-discovered
from the pid via lsof):
conveyor register-worker --name foo --pid <WORKER_PID> \
--cwd "$PWD" --tmux-session codex-foo
When a Codex app tool created or identified the thread, preserve that identity with optional metadata flags:
conveyor register-worker --name foo --pid <WORKER_PID> \
--cwd "$PWD" --codex-app-thread-id <THREAD_ID> \
--codex-app-thread-title "Human readable title"
If lsof discovery fails, pass the rollout path explicitly:
conveyor register-worker --name foo --pid <WORKER_PID> \
--cwd "$PWD" --tmux-session codex-foo \
--codex-session ~/.codex/sessions/.../rollout-...-<uuid>.jsonl
Register a manager (tmux not required):
conveyor register-manager --name foo-mgr --pid <MGR_PID> --cwd "$PWD"
For new manager sessions started by Agent Conveyor, prefer start-manager or
pair. These send a manager bootstrap prompt to Codex so the rollout JSONL is
opened during startup and the manager has setup context. In pair, the manager
prompt includes the task name, goal, worker session, manager-config <task> --questions, and cycle <task>.
For late attach, pass the known task context directly:
conveyor start-manager --name foo-mgr --cwd "$PWD" \
--task foo-task --task-goal "..." --worker foo-worker
That bootstrap starts with concrete manager-config, cycle, manager-ack,
and worker-ack commands instead of <task> placeholders. If manager config
has already been recorded for the task, the bootstrap tells the manager to start
with cycle.
List registered sessions:
conveyor sessions
conveyor sessions --role worker
conveyor sessions --role manager
Create A Task And Bind
For automated bootstrap of a fresh supervised worker/manager pair, use pair
instead of manually starting and binding sessions:
conveyor pair \
--task <task-slug> \
--worker-name <worker-name> \
--manager-name <manager-name> \
--dispatcher-id dispatch-pair \
--cwd <target-repo> \
--codex-profile yolo \
--manager-mode strict \
--task-goal "<one-line goal>" \
--task-prompt "<worker prompt>" \
--manager-objective "<manager objective>" \
--manager-acceptance "<finish criterion>"
Use --no-dispatch only for isolated tests or manual Dispatch supervision.
Use this for external dogfood runs. Keep the control repo as the command cwd,
but set --cwd to the downstream project:
cd /Users/neonwatty/Desktop/codex-terminal-manager
export DOGFOOD_CWD="/path/to/external/project"
export TASK="external-dogfood-$(date +%Y%m%d)"
export WORKER="dogfood-worker-$(date +%Y%m%d)"
export MANAGER="dogfood-manager-$(date +%Y%m%d)"
conveyor pair \
--task "$TASK" \
--worker-name "$WORKER" \
--manager-name "$MANAGER" \
--cwd "$DOGFOOD_CWD" \
--codex-profile yolo \
--manager-mode strict \
--task-goal "Complete one small real task in the external project." \
--task-prompt "Pick one small, concrete improvement. Keep changes scoped. Run verification. Report files changed and commands run." \
--manager-objective "Supervise the worker, request acceptance criteria and evidence, and finish only when verified." \
--manager-acceptance "The task is complete, verified, and summarized with files changed and commands run."
During external dogfood, review telemetry every few cycles:
conveyor cycle "$TASK"
conveyor criteria "$TASK" --list
conveyor telemetry --summary --task "$TASK"
conveyor telemetry --task "$TASK"
conveyor telemetry --search manager --task "$TASK"
conveyor replay "$TASK"
Finish and export evidence:
conveyor finish-task "$TASK" \
--capture-transcript-before-stop \
--require-transcript-segment \
--require-criteria-audit \
--stop-manager \
--stop-worker
conveyor export-task "$TASK" \
--output "/tmp/$TASK-export" \
--zip \
--include-transcripts
conveyor sessions --state active
conveyor reconcile --stale-cycles-seconds 1
conveyor tasks --create my-task --goal "Refactor auth"
conveyor handoff my-task \
--summary "Worker explored the current auth flow and found middleware drift." \
--next-step "Implement the middleware cleanup from docs/auth-plan.md"
conveyor manager-config my-task \
--mode guided \
--objective "Keep the worker aligned to docs/auth-plan.md" \
--reference docs/auth-plan.md \
--acceptance "Tests pass" \
--guideline "Nudge only when the worker is idle, stale, or blocked"
conveyor bind --task my-task --worker foo --manager foo-mgr
tasks lists or creates rows. bind ties the worker and manager sessions to
the task. The task is now active.
Use handoff before or during management promotion to save the worker's
compact progress summary and likely next steps in SQLite. Use manager-config
to save what the manager should check against, how structured supervision
should be, acceptance criteria, planning/PRD/mockup references, and permissions
such as --allow-pr, --allow-merge-green, and
--allow-worker-compact-clear. For ship-it loops, also use explicit
--permit repo.push_branch, --permit repo.monitor_ci, and
--permit repo.resolve_conflicts; never imply these from a general request to
"ship it."
Manager Recipes
When the user's setup request is broad or freeform, resolve it to one named
manager recipe before starting supervision, or explicitly label it custom.
Use docs/manager-recipes.md as the canonical reference, and use
conveyor manager-recipes --list --json or
conveyor manager-recipes --show <recipe> --json for stable setup metadata.
First-draft recipes:
GoalBuddy Conveyor— one parent board, one active child board, PR/CI/merge orsatisfied_on_mainproof, and parent receipt update before the next child.Creative Ops Campaign— one manager supervises multiple named worker slots for channel-specific creative assets using campaign state, dashboard status, assignment receipts, asset review receipts, and human publish gates.Test Coverage Loop— require test coverage evidence plus structured adversarial proof before another worker pass.UX Polish Loop— require browser/screenshot/visual-diff evidence plus structured adversarial proof before another visual pass.Nudge / What's Next Manager— observe, ask status and criteria questions, nudge sparingly, and keep permissions minimal.PR/CI/Merge Ralph Loop— manage PR readiness, CI, fixes, merge, handoff, and compact/clear receipts.Autonomous Ship-It Loop— push branch, open PR, monitor CI, handle bounded conflicts, and merge only after manager-owned mergeability, merge decision, post-merge, and adversarial evidence.
Support patterns:
Inbox / No-Tmux App Loop— usemanager-inboxandworker-inboxfor Codex app sessions that cannot receive tmux pushes.Recovery / Resume / Handoff— inspect saved config, handoff, replay, audit, telemetry, and inbox state before continuing a task.
Before saving config or cutting the manager loose, show a locked setup summary
with the selected recipe, mode, permissions, tools, epilogues, cleanup policy,
evidence gates, disallowed actions, and whether the user confirmed it. Then
persist the settings with conveyor manager-config.
When setting up a manager from inside a manager Codex session, prefer:
conveyor manager-config my-task --questions
Read the JSON question schema, ask the user those questions in the manager
conversation, then persist the answers with manager-config flags. This keeps
the human interaction in the Codex chat where the user is already working and
keeps SQLite writes explicit. Use manager-config --interactive only as a
terminal fallback for a human running conveyor directly.
Before instructing high-level actions such as branch push, PR creation, CI monitoring, conflict resolution, green PR merge, or worker compact/clear, check the saved policy:
conveyor manager-permission my-task repo.push_branch --require
conveyor manager-permission my-task repo.open_pr --require
conveyor manager-permission my-task repo.monitor_ci --require
conveyor manager-permission my-task repo.resolve_conflicts --require
conveyor manager-permission my-task repo.merge_green_pr --require
conveyor manager-permission my-task worker_compact_clear \
--require-handoff --require
Use --require for fail-closed behavior. Use --require-handoff before
compact/clear so the worker's visible progress is saved first.
To request worker compaction/clear through the audited path, prefer the one-command wrapper:
conveyor compact-worker my-task \
--reason "Worker context should be compacted after handoff"
Use --clear for /clear. For lower-level control, first record a nudge
manager decision, then run:
decision_id=$(conveyor record-decision my-task nudge \
--reason "Worker context should be compacted after handoff" \
| node -e 'const fs = require("fs"); console.log(JSON.parse(fs.readFileSync(0, "utf8")).id)')
conveyor request-worker-compact my-task \
--decision-id "$decision_id" --strict-decisions
This command checks worker_compact_clear, requires a saved handoff, records a
durable command, and sends Codex /compact to the worker's tmux pane. Use
--clear for /clear, or --prompt-only to send the conservative
verify/update-handoff prompt instead of a slash command.
For Codex app workers, use app worker rotation instead of this tmux slash path:
conveyor app-worker-rotation-plan my-task --old-worker-thread-id <active-worker-thread-id> --require-handoff --json, followed by Codex app
create_thread, exact old-worker set_thread_archived, and
app-worker-rotation-record.
Manager Loop Pattern
The manager Codex drives supervision by calling conveyor cycle <task> in a
loop. Each cycle is idempotent: it ingests only new bytes from the rollout
JSONL, computes worker state from the JSON event stream, captures the worker
tmux pane, and returns a JSON dict.
Before declaring work complete, try to disprove the change. Identify the strongest realistic failure mode, verify it with a command, test, trace, screenshot, audit record, diff, or direct inspection, and include that evidence in the handoff. Do not accept worker claims, passing happy-path tests, generated summaries, or optimistic UI as proof by themselves. Treat unverified assumptions as blockers or explicit follow-ups.
When the repository being managed is CTM, see
docs/agent-evidence-playbook.md for CTM-specific evidence choices and final
handoff format.
Natural-language requests such as "run this as an adversarially gated loop",
"require adversarial proof before another worker iteration", or "do not finish
until you have tried to disprove it" should be treated as operational gate
requests only after conveyor loop-triggers --classify "<prompt>" --json matches a controlled trigger. For Ralph-loop work, create or use a loop
policy whose required_before_continue includes adversarial_check, then
record each proof receipt with conveyor loop-evidence adversarial-check <task> --loop-run <run-id> --iteration <n> --failure-mode ... --check ... --result .... For final completion, use conveyor finish-task <task> --require-adversarial-proof so the task cannot be marked
done until structured proof exists. Use conveyor qa-run adversarial-triggers --receipt-output /tmp/adversarial-triggers-receipt.json --json to verify the controlled trigger path.
Natural-language requests such as "create an autonomous GoalBuddy conveyor" or
"split this into vertical-slice child GoalBuddy boards and continue until all
are merged or proven satisfied" should be treated as conveyor requests, not as a
flat task list. Use conveyor qa-plan goalbuddy-conveyor to retrieve
the reusable starter prompt, authority boundaries, acceptance criteria,
correlation markers, and negative QA checks. The manager should keep exactly one
child board active, require PR/CI/merge or satisfied_on_main proof before
marking a child done, and update the parent receipt before activating the next
child.
Natural-language requests such as "run a multi-channel creative ops campaign", "have one manager coordinate YouTube, TikTok, LinkedIn, and Facebook workers", "generate imagery, HyperFrames, videos, and copy across channels", or "use multiple first-time manager guys and girls" should resolve to the Creative Ops Campaign recipe unless the user chooses a custom policy. The manager should:
- Create or reuse a campaign with
conveyor campaign create. - Create one worker slot per channel or discipline with
campaign add-slot, preserving Codex app thread ids/titles when available. - Record structured channel briefs with
campaign brief. - Assign work with
campaign assignbefore asking a worker to produce assets. - Record worker output with
campaign assetusing sanitized prompt summaries, asset type, review status, and safe artifact paths. - Use
campaign dashboard --name <campaign> --jsonordashboard --campaign <campaign>as the manager status surface. - Use
campaign closeout --name <campaign> --jsonfor the final read-only manager/operator handoff: verdict, worker thread ids, blockers, receipt counts by assignment, proof checks, and failure-mode evidence. - Rotate stale/context-heavy workers only with
campaign rotate-slotand an exact--expected-thread-id; archive only withcampaign archive-slotand the exact current thread id. - Treat public publishing, scheduling, posting, external account access, private phone content, raw audio, tokens, JWTs, keys, archives, and IPAs as human-gated or forbidden unless the operator explicitly approves the narrow action.
Do not claim that the campaign system is fully dogfooded merely because the recipe was configured. Final campaign dogfood requires dashboard proof, asset receipts, manager review decisions, and a separate dogfood receipt or blocker.
Natural-language requests such as "ship this autonomously", "let the manager
open the PR and merge when green", or "have the manager handle conflicts and
merge" must resolve to the ship-it-loop recipe or an explicit custom policy.
Use conveyor qa-plan ship-it-loop to retrieve the permission boundaries,
correlation markers, lifecycle evidence template, and negative QA checks. The
manager must record branch_ready, branch_pushed, pr_url, ci_green,
mergeability_clean, manager_merge_decision, merge,
post_merge_verification, and structured adversarial_check evidence before
treating the loop as merge-ready. CI green is not a merge decision.
conveyor cycle my-task
# {
# "kind": "session_cycle",
# "task": "my-task",
# "state": "busy" | "idle" | "unknown",
# "staleness_seconds": 4.2,
# "notable_pane_pattern": "trust_prompt" | null,
# "pane_signal": { "captured": true, "classifier": {...} },
# "manager_context": {
# "manager_config": {...},
# "worker_handoff": {...},
# "acceptance_criteria": {
# "summary": {"proposed": 1, "accepted": 2, "satisfied": 0, "deferred": 1, "rejected": 0},
# "open": [...],
# "proposed": [...],
# "satisfied": [...],
# "deferred": [...],
# "rejected": [...]
# },
# "criteria_negotiation": {
# "needed": true,
# "reason": "no_criteria",
# "prompt": "Please propose 2-4 acceptance criteria for the current slice...",
# "suggested_actions": [...]
# }
# },
# "ingest": { "new_events": 3, "new_offset": 12345 },
# "cycle_id": 17,
# ...
# }
Loop pseudo-pattern:
while task is active:
result = conveyor cycle <task> # observe
interpret result.state, result.staleness_seconds, result.notable_pane_pattern
decide:
- "wait" -> sleep, then loop
- "nudge" -> conveyor session-nudge <worker> "<text>"
- "interrupt" -> conveyor session-interrupt <worker>
- "escalate" -> conveyor finish-task <task> --reason "<why>"
Interpretation guidance:
state: "busy"and recent activity: wait.state: "idle"and the worker is at a prompt: send asession-nudgewith the next instruction.notable_pane_patternis non-null: branch on it directly. For example, atrust_promptorenter_to_confirmmay want a single Enter sent viasession-nudge ""(Enter is always appended).- Long
staleness_secondswith no notable pattern: send a status nudge before interrupting. - Clear busy-wait pattern or explicit user request:
session-interrupt.
Acceptance criteria are living supervision state, not just setup text. Inspect
manager_context.acceptance_criteria every cycle:
- Treat
openas accepted criteria that still need worker proof before the task can finish. - Inspect
manager_context.criteria_negotiationevery cycle. Whenneededis true, use itspromptas the worker nudge or adapt it to the situation before recording criteria. - When worker progress reveals new edge cases, missing tests, polish needs, or scope boundaries, ask the worker to propose which criteria are must-have now versus follow-up.
- Record current-task criteria as proposed or accepted, and record follow-up criteria as deferred.
- Keep manager closeout/control-plane proof out of accepted worker criteria.
Items such as
finish-task,--require-criteria-audit, final task state, heartbeat teardown, and final manager reporting belong in the manager final report, audit, replay, or epilogue evidence unless the task is explicitly Conveyor closeout QA. - Use
conveyor criteriato accept, satisfy, defer, or reject criteria as evidence accumulates. - Before finishing, compare the worker's receipts and verification against all accepted open criteria.
Criteria command examples:
conveyor criteria my-task --list
conveyor criteria my-task --add --criterion "..." --source worker_proposed --status proposed
conveyor criteria my-task --accept 12 --rationale "Must-have for this task"
conveyor criteria my-task --satisfy 12 --evidence-json '{"command":"npm test -- --runInBand","status":"pass"}'
conveyor criteria my-task --defer 13 --rationale "Follow-up after this task"
conveyor criteria my-task --reject 14 --rationale "Duplicate or out of scope"
Replace placeholder ... values with the actual criterion and verification
command. To add a criterion and satisfy that same row after verification:
criterion_id=$(conveyor criteria my-task --add --criterion "Targeted prompt tests pass" --source worker_proposed --status proposed | node -e 'const fs = require("fs"); console.log(JSON.parse(fs.readFileSync(0, "utf8")).affected_criterion.id)')
conveyor criteria my-task --satisfy "$criterion_id" --evidence-json '{"command":"npm test -- --runInBand","status":"pass"}'
When making multiple criteria changes, use each mutation response's
affected_criterion as the row receipt, then run conveyor criteria <task> --list before finishing or making an audit decision.
Sample nudge:
conveyor session-nudge foo \
"Your latest progress exposed extra edge cases. Please propose acceptance criteria split into must-have for this task versus follow-up, and include the verification you expect for each."
Actuation
Nudge the worker (sends text plus Enter to the worker's tmux pane). Only worker sessions can be nudged this way; managers running outside tmux cannot:
conveyor session-nudge foo "Please update status and continue."
conveyor session-nudge foo "Status?" --dry-run
Send an interrupt key (default C-c):
conveyor session-interrupt foo
conveyor session-interrupt foo --key C-c --followup "continue with the smaller refactor"
Inspect, Replay, Audit
conveyor tail foo --limit 30
conveyor tail foo --subtype agent_message
conveyor divergences my-task --limit 20
conveyor audit my-task
conveyor replay my-task
conveyor replay my-task --format transcript --limit 40
conveyor replay my-task --format full-transcript --include-content --limit 40 > /tmp/my-task-full-transcript.txt
tailprints recent ingested rollout events for a session.divergenceslists cycles where the shadow pane signal flagged a notable pattern (trust prompt, rate-limit prompt, approval prompt, ...).- Raw transcript/log content should not be printed inside an active Codex
terminal. Prefer compact/timeline/transcript summaries; redirect any command
using
--include-contentto a file. auditlistseventsrows for the task; cycle observations show up viareplayand themanager_cyclestable.replayreconstructs the task chronologically. Use--format compactfor decisions and side effects,--format transcriptfor deduplicated terminal excerpts,--format full-transcriptonly for debugging.
Finish, Unbind, Deregister
When the task is complete:
conveyor finish-task my-task --reason "auth refactor merged"
conveyor finish-task my-task --reason "..." --require-criteria-audit
conveyor finish-task my-task --reason "..." --stop-manager
conveyor finish-task my-task --reason "..." --stop-worker
finish-task marks the task done and leaves both sessions running by default.
Use --require-criteria-audit when final acceptance criteria should be enforced:
it fails before finishing if any task criteria remain accepted; proposed,
satisfied, deferred, and rejected criteria do not block.
Add --stop-manager / --stop-worker only when the user explicitly wants the
tmux session torn down.
Clean up the binding and session registrations:
conveyor unbind --task my-task
conveyor deregister foo
conveyor deregister foo-mgr
deregister refuses if a session is still bound to an active task; run
unbind first.
Reconcile Runtime Drift
If something looks wrong — a worker process exited, a manager left a session behind, a task has stopped getting cycle rows — run reconcile:
conveyor reconcile
conveyor reconcile --apply
Without --apply it prints a JSON report of dead-pid sessions, dangling
bindings, and stuck tasks. With --apply it marks dead-pid sessions
state='gone' and dangling bindings state='invalid', writing audit events
for each mutation. Stuck tasks are reported but never auto-closed.
For schema-level checks (legacy workers/managers tables, missing tables,
etc.) run conveyor db-doctor --live.
Natural-Language Command Mapping
- "set up a Codex app Ralph loop": if the current session has Codex app thread
tools, call
create_threadfor fresh same-project manager and worker threads unless the current thread is explicitly the manager, set readable titles, runconveyor create-disposable-bindingwith both--manager-codex-app-thread-id/--manager-codex-app-thread-titleand--worker-codex-app-thread-id/--worker-codex-app-thread-title, then send the returned bootstrap prompts usingsend_message_to_thread. If those tools are unavailable, runcreate-disposable-bindingwithout thread metadata and give the user the handoff prompts to paste into manually opened sessions. Keep Dispatch as the source of durable communication, and make both Codex app sessions repeat their role-specificapp-heartbeatcommand while idle. Useapp-loop-statusandapp-wakeup-planfor operator status and stale-thread recovery, andapp-wakeup-dispatchwhen the manager needs an auditable prepared/skipped/blocked wake receipt. Prefer generatedheartbeat_recommendations.wakeup_dispatch_commandandheartbeat_recommendations.delivery_receipt_commandswhen present. If the current manager is a Codex app session with thread tools, send onlysend_ready=truewake prompts withsend_message_to_thread, then record the outcome withapp-wakeup-record-delivery. - "fresh worker", "clear app worker", or "rotate Codex app worker": require a
saved worker handoff, run
app-worker-rotation-planwith the active bound worker thread id, create a new same-project worker thread from the emitted prompt, archive only the exact old worker thread id emitted by the plan, and record withapp-worker-rotation-record. If the plan is blocked or the old id does not exactly match the active bound worker, stop and report the blocker. - "set up a Creative Ops Campaign", "run a multi-channel campaign", or "one
manager with multiple channel workers": use the Creative Ops Campaign recipe;
create or reuse a campaign, add one slot per channel or discipline, record
briefs, assign work before worker prompts, require
campaign assetreceipts for outputs, runcampaign dashboard --name <campaign> --jsonfor manager status, and require exact-threadcampaign rotate-slot/campaign archive-slotfor owned worker cleanup. Do not publish or schedule without explicit human approval. - "set up an autonomous ship-it loop", "ship this through PR and merge", or
"merge when green": resolve to
ship-it-loopunless the user explicitly chooses a custom policy. Show the locked recipe summary, require explicit operator confirmation forrepo.push_branch,repo.open_pr,repo.monitor_ci,repo.resolve_conflicts, andrepo.merge_green_pr, then create or use aship_it_looprun. Before merge, require fresh CI, mergeability, manager merge decision, merge receipt, post-merge verification, and adversarial proof receipts. If conflict retries hit the configured limit, block with sanitized evidence and the next exact action instead of continuing to wake the worker. - "register this Codex session as the worker for dashboard setup ":
derive
dashboard-<CODE>-worker, runconveyor doctor-self, thenconveyor register-worker --name dashboard-<CODE>-worker --pid <PID> --cwd <CWD> --tmux-session <SESSION>. - "register this session as the manager for dashboard setup ":
derive
dashboard-<CODE>-manager, runconveyor register-manager --name dashboard-<CODE>-manager --pid <PID> --cwd <CWD>. - "register this Codex session as a worker": choose a concise worker name if
none was provided, then run
conveyor doctor-selfandregister-worker. - "register a manager": choose a concise manager name if none was provided,
then run
conveyor register-manager. - "create a task and bind these sessions":
conveyor tasks --create <TASK> --goal "<goal>"thenconveyor bind --task <TASK> --worker <W> --manager <M>. - "watch the worker", "supervise this task", "run a cycle":
conveyor cycle <TASK>(in a loop). - "send a nudge", "ask the worker something":
conveyor session-nudge <WORKER> "<text>". - "interrupt the worker":
conveyor session-interrupt <WORKER>. - "what happened in this task", "show the replay":
conveyor replay <TASK>(optionally with--format). - "finish this task":
conveyor finish-task <TASK> --reason "<why>". - "unbind", "deregister this session":
conveyor unbind --task <TASK>followed byconveyor deregister <NAME>per session. - "reconcile drift", "something looks stale":
conveyor reconcile(add--applyif the dry-run report looks correct).
QA Plan
For a repeatable end-to-end checklist:
conveyor qa-plan self-management
conveyor qa-plan self-management --json
conveyor qa-plan emergent-criteria
conveyor qa-plan emergent-criteria --json
conveyor qa-plan tmux-errors
conveyor qa-plan tmux-errors --json
conveyor qa-plan ship-it-loop
conveyor qa-run ship-it-loop --receipt-output /tmp/ship-it-loop-receipt.json --json
Use emergent-criteria when validating a real worker/manager pair through
criteria negotiation, audited finish gating, replay/export, and
--stop-manager --stop-worker cleanup.
Use tmux-errors when validating read-only JSON degradation, mutating command
failures, pane capture degradation, stop failures, and reconcile recovery for
disposable tmux failure scenarios.