Summary
LazyCodex / OMO subagent orchestration can spawn planning, review, or audit agents and then move dependent work forward without collecting or integrating their result.
The user-visible failure is that root agents appear to "send a plan/review agent and naturally move on" instead of enforcing the documented short wait_agent polling + follow-up + inconclusive/respawn behavior before marking dependent work complete.
Environment
- LazyCodex version:
lazycodex-ai 4.7.5
- Codex version:
codex-cli 0.137.0
- OS: macOS 26.5.1 (Build 25F80)
- Install method:
npx lazycodex-ai / local OMO plugin cache
- Relevant config: OMO plugin enabled; bundled rules inject Hephaestus / ultrawork / review-work guidance. Relevant skill docs:
skills/review-work/SKILL.md: says plan/reviewer agents should be spawned in background, root should poll with short wait_agent cycles, follow up after two waits with no substantive result, and not count silent/ack-only lanes as pass.
components/ultrawork/directive.md: same subagent reliability guidance.
Reproduction
This was observed from real LazyCodex session logs, not a synthetic repro.
- Run a multi-step task that spawns a dependent audit/research/planning subagent.
- Inspect the resulting session JSONL for
spawn_agent, wait_agent, send_message/followup_task, close_agent, and update_plan ordering.
- Observe cases where the root agent marks dependent steps complete, starts implementation, runs tests, or writes a plan without waiting for spawned subagents.
Expected Behavior
After spawning a subagent whose deliverable informs a dependent decision, root should:
- Continue only genuinely independent work while the child runs.
- Poll with
wait_agent short cycles.
- After two non-substantive waits, send a targeted heartbeat/follow-up such as
TASK STILL ACTIVE: return <deliverable> or BLOCKED: <reason>.
- Integrate the child result, or mark the lane inconclusive and close/respawn smaller.
- Only then mark the dependent plan step complete, begin dependent implementation, approve/finalize, or generate a final plan.
Actual Behavior
Observed timelines show dependent transitions before child results are collected.
Case 1: audit subagent spawned, dependent implementation proceeds before result
Session:
~/.codex-lazycodex/sessions/2026/06/05/rollout-2026-06-05T16-03-00-019e9697-a1ad-7163-9e9f-7b1e0e73f271.jsonl
Relevant timeline:
2026-06-05T07:20:50Z update_plan: "부위 선택 정의와 저장 규칙의 전체 감사 기준 수립" in_progress
2026-06-05T07:21:47Z spawn_agent body_region_audit
2026-06-05T07:22:16Z update_plan: "앞서 언급한 의심 부위와 유사 앞/뒤 미분리 사례 확인" completed; "필요한 회귀 테스트/수정 적용" in_progress
2026-06-05T07:22:59Z exec_command: npm run test:run ...
2026-06-05T07:25:34Z exec_command: next dev --turbo -p 3002
2026-06-05T07:25:49Z exec_command: Playwright E2E / manual browser QA
2026-06-05T07:24:34Z wait_agent timeout_ms=30000
2026-06-05T07:26:33Z close_agent body_region_audit
2026-06-05T07:26:39Z update_plan: all steps completed
The audit agent was supposed to find body-region coverage issues "similar to neck front/back not being distinguishable." The root moved into dependent implementation/testing before collecting and integrating that audit result.
Case 2: planning/research agents spawned, no wait/follow-up/close in the session
Session:
~/.codex-lazycodex/sessions/2026/06/05/rollout-2026-06-05T17-15-48-019e96da-49f1-76b3-9796-956e482e42bd.jsonl
Relevant timeline:
2026-06-05T08:16:28Z spawn_agent internal_thread_usage
2026-06-05T08:16:28Z spawn_agent internal_emr_sync
2026-06-05T08:16:29Z spawn_agent internal_schema_ownership
2026-06-05T08:16:29Z spawn_agent internal_tests
2026-06-05T08:16:29Z spawn_agent external_graphql_sync
2026-06-05T08:16:29Z spawn_agent external_postgres_migrations
2026-06-05T08:19:02Z spawn_agent gap_analysis
2026-06-05T08:21:53Z exec_command: mkdir -p .omo/plans .omo/evidence
2026-06-05T08:25:18Z exec_command: verifies .omo/plans/emr-patient-thread-sync-legacy-archive.md exists and has tasks
The same session contains no wait_agent, followup_task, send_message heartbeat, or close_agent calls for these seven spawned agents. This means the plan can be generated/verified without the required research/metis lanes being integrated.
Contrasting case: wait loop behavior exists but is not consistently enforced
Session:
~/.codex-lazycodex/sessions/2026/06/05/rollout-2026-06-05T10-40-35-019e9570-7330-7ff1-a152-926a5c67974b.jsonl
Relevant timeline:
2026-06-05T03:25:13Z spawn_agent operation_registry_plan_agent
2026-06-05T03:26:06Z wait_agent timeout_ms=10000
2026-06-05T03:26:13Z wait_agent timeout_ms=10000
2026-06-05T03:26:44Z wait_agent timeout_ms=10000
2026-06-05T03:26:57Z send_message TASK STILL ACTIVE ...
2026-06-05T03:27:01Z wait_agent timeout_ms=10000
2026-06-05T03:27:13Z close_agent operation_registry_plan_agent previous_status=running
2026-06-05T03:27:21Z spawn_agent operation_registry_ordering_small
This shows the desired pattern is possible in the runtime/tooling, but not reliably applied across sessions.
Evidence
Commands used to confirm the behavior:
jq -r 'select(.type=="response_item" and .payload.type=="function_call" and (.payload.name|IN("spawn_agent","wait_agent","send_message","followup_task","close_agent","update_plan","exec_command"))) | [.timestamp,.payload.name, (.payload.arguments|fromjson? // .payload.arguments)] | @json' ~/.codex-lazycodex/sessions/2026/06/05/rollout-2026-06-05T16-03-00-019e9697-a1ad-7163-9e9f-7b1e0e73f271.jsonl
jq -r 'select(.type=="response_item" and .payload.type=="function_call" and (.payload.name|IN("spawn_agent","wait_agent","send_message","followup_task","close_agent","exec_command"))) | [.timestamp,.payload.name, (.payload.arguments|fromjson? // .payload.arguments)] | @json' ~/.codex-lazycodex/sessions/2026/06/05/rollout-2026-06-05T17-15-48-019e96da-49f1-76b3-9796-956e482e42bd.jsonl
gh issue list --repo code-yeongyu/lazycodex --search "wait_agent" --state open
gh issue list --repo code-yeongyu/lazycodex --search "subagent" --state open
gh issue list --repo code-yeongyu/lazycodex --search "dependent work complete spawned agent" --state open
Duplicate search returned no matching open issue for wait_agent, subagent, reviewer agent, dependent work complete spawned agent, plan review agent wait, or wait_agent close_agent.
Root Cause
Strongly evidenced root cause: the waiting/integration rule is prompt-level guidance, not a runtime guard.
The current guidance tells agents to poll, follow up, close/respawn, and not count silent lanes as PASS, but nothing appears to prevent these unsafe transitions while child agents are still active:
- marking a plan item completed when a spawned child owns evidence for that item
- moving from discovery/audit to implementation before the audit lane returns
- generating/verifying a plan before research/metis lanes return
- finalizing with open child agents
Alternative hypotheses considered:
- The child agents completed but their results were omitted from the sampled timeline. Refuted for Case 2 by the absence of any
wait_agent, send_message, followup_task, or close_agent calls in the session.
- The root only performed independent work while children ran. Refuted for Case 1 by
update_plan marking the dependent audit/discovery step complete and starting implementation/tests before collecting the audit.
wait_agent exists but is unavailable. Refuted by the operation-registry session where wait_agent and heartbeat messages were used.
Proposed Fix
Add a runtime or hook-level subagent barrier for dependent transitions.
Possible implementation approaches:
- Track active child agents spawned by the current root turn/session.
- Before
update_plan marks a dependent step complete, before final answer, and before plan-generation completion, warn or block if relevant child agents are still active and no result/inconclusive status has been recorded.
- Provide a lightweight helper state such as
spawn_agent(..., blocks_step="...") or infer from spawned task names/messages when no explicit step exists.
- Add a Stop-hook/PostToolUse check that detects active children at finalization or step completion and injects a hard warning: "active subagents not integrated; wait/close/mark inconclusive first."
- Normalize guidance to prefer
wait_agent polling with visible heartbeat status, not silent progress into dependent work.
Likely affected components:
- Codex subagent orchestration wrappers / hooks in the OMO plugin
review-work and ulw-plan/ultrawork injected guidance
- Any LazyCodex state layer that can observe
spawn_agent, wait_agent, close_agent, and update_plan
Verification Plan
- Reproduce with a test harness/session that spawns a slow planning/audit agent, then attempts to mark the owning step complete before
wait_agent returns.
- Verify the runtime blocks or warns on the unsafe transition.
- Verify independent root work is still allowed while the child is active.
- Verify final answer/plan completion is blocked or warned while child agents remain active.
- Regression check a valid flow: spawn child, do independent reads,
wait_agent, integrate result, close child, then mark dependent step complete.
Summary
LazyCodex / OMO subagent orchestration can spawn planning, review, or audit agents and then move dependent work forward without collecting or integrating their result.
The user-visible failure is that root agents appear to "send a plan/review agent and naturally move on" instead of enforcing the documented short
wait_agentpolling + follow-up + inconclusive/respawn behavior before marking dependent work complete.Environment
lazycodex-ai 4.7.5codex-cli 0.137.0npx lazycodex-ai/ local OMO plugin cacheskills/review-work/SKILL.md: says plan/reviewer agents should be spawned in background, root should poll with shortwait_agentcycles, follow up after two waits with no substantive result, and not count silent/ack-only lanes as pass.components/ultrawork/directive.md: same subagent reliability guidance.Reproduction
This was observed from real LazyCodex session logs, not a synthetic repro.
spawn_agent,wait_agent,send_message/followup_task,close_agent, andupdate_planordering.Expected Behavior
After spawning a subagent whose deliverable informs a dependent decision, root should:
wait_agentshort cycles.TASK STILL ACTIVE: return <deliverable> or BLOCKED: <reason>.Actual Behavior
Observed timelines show dependent transitions before child results are collected.
Case 1: audit subagent spawned, dependent implementation proceeds before result
Session:
~/.codex-lazycodex/sessions/2026/06/05/rollout-2026-06-05T16-03-00-019e9697-a1ad-7163-9e9f-7b1e0e73f271.jsonlRelevant timeline:
The audit agent was supposed to find body-region coverage issues "similar to neck front/back not being distinguishable." The root moved into dependent implementation/testing before collecting and integrating that audit result.
Case 2: planning/research agents spawned, no wait/follow-up/close in the session
Session:
~/.codex-lazycodex/sessions/2026/06/05/rollout-2026-06-05T17-15-48-019e96da-49f1-76b3-9796-956e482e42bd.jsonlRelevant timeline:
The same session contains no
wait_agent,followup_task,send_messageheartbeat, orclose_agentcalls for these seven spawned agents. This means the plan can be generated/verified without the required research/metis lanes being integrated.Contrasting case: wait loop behavior exists but is not consistently enforced
Session:
~/.codex-lazycodex/sessions/2026/06/05/rollout-2026-06-05T10-40-35-019e9570-7330-7ff1-a152-926a5c67974b.jsonlRelevant timeline:
This shows the desired pattern is possible in the runtime/tooling, but not reliably applied across sessions.
Evidence
Commands used to confirm the behavior:
Duplicate search returned no matching open issue for
wait_agent,subagent,reviewer agent,dependent work complete spawned agent,plan review agent wait, orwait_agent close_agent.Root Cause
Strongly evidenced root cause: the waiting/integration rule is prompt-level guidance, not a runtime guard.
The current guidance tells agents to poll, follow up, close/respawn, and not count silent lanes as PASS, but nothing appears to prevent these unsafe transitions while child agents are still active:
Alternative hypotheses considered:
wait_agent,send_message,followup_task, orclose_agentcalls in the session.update_planmarking the dependent audit/discovery step complete and starting implementation/tests before collecting the audit.wait_agentexists but is unavailable. Refuted by the operation-registry session wherewait_agentand heartbeat messages were used.Proposed Fix
Add a runtime or hook-level subagent barrier for dependent transitions.
Possible implementation approaches:
update_planmarks a dependent step complete, before final answer, and before plan-generation completion, warn or block if relevant child agents are still active and no result/inconclusive status has been recorded.spawn_agent(..., blocks_step="...")or infer from spawned task names/messages when no explicit step exists.wait_agentpolling with visible heartbeat status, not silent progress into dependent work.Likely affected components:
review-workandulw-plan/ultraworkinjected guidancespawn_agent,wait_agent,close_agent, andupdate_planVerification Plan
wait_agentreturns.wait_agent, integrate result, close child, then mark dependent step complete.