Description
Make Codex emit per-agent SubagentStart context when a subagent's SessionStart fires. Today hook_codex.py::_handle_agent_session_start identifies the subagent (via payload markers or transcript session_meta) and calls record_subagent_start, but intentionally skips additional-context emission — the module docstring says "Subagent sessions skip the main-session workflow authoring policy injection." That blanket skip was correct when no per-agent template existed; with issue-planner (#145) merged, Codex subagents now silently miss the <runbook> block and other per-agent context that Claude subagents inherit via SubagentStart.
The shared builder build_subagent_start_context(config, plugin_root, runtime, agent_type) already supports runtime="codex" and renders the codex launcher snippet — the infrastructure is in place. Only the Codex hook adapter declines to call it.
Note: the contract here is not main-session authoring policy. It is the narrower per-agent slice — launcher + authoring resolver + per-agent runbook — that subagents need to operate. The Codex hook should inherit the same scope Claude's SubagentStart inherits today, no more.
Approach
- Extend
_handle_agent_session_start in plugins/workflow/hooks/scripts/hook_codex.py:
- After
record_subagent_start, call build_subagent_start_context(config, _plugin_root(), runtime="codex", agent_type=<resolved name>).
- Resolve
agent_type from transcript metadata via the existing _agent_name_from_metadata helper.
- Emit
hookSpecificOutput (hookEventName: "SessionStart", additionalContext: <block>) when a non-empty block is produced.
- Keep the early return when metadata is missing or
parent_thread_id cannot be resolved — no metadata, no agent block.
- Update the module docstring at the top of
hook_codex.py so it no longer claims Codex subagents skip all injection; describe the new contract.
- Update
plugins/workflow/hooks/AGENTS.md:
- Rewrite "Subagents inherit env, not policy." — subagents also inherit the per-agent launcher / authoring-resolver / runbook fragments via
build_subagent_start_context, but still not the heavier main-session authoring guidance.
- Keep "Manifest asymmetry is deliberate. Codex registers only SessionStart + UserPromptSubmit." — add a sentence noting that Codex routes the SubagentStart equivalent through
SessionStart's subagent branch.
- Tests:
- If existing tests in
plugins/workflow/tests/test_workflow_hook.py assert that Codex agent SessionStart emits no additionalContext, flip the assertion: emit when agent_type matches a known per-agent template; stay silent for unknown agents.
- Add a positive test exercising
issue-planner via a synthesized Codex session_meta and verifying the rendered block carries the expected <runbook> tag.
- Add a negative test: Codex subagent
SessionStart with no resolvable agent name still records identity but emits no additional context.
Affected Paths
plugins/workflow/hooks/scripts/hook_codex.py
plugins/workflow/hooks/AGENTS.md
plugins/workflow/tests/test_workflow_hook.py
Unit Test Strategy
- The existing test helper renders per-agent context via
build_subagent_start_context for Claude. Mirror the same shape for Codex with a fixture session_meta that names the agent (agent_name: "issue-planner", or the equivalent slot under source.subagent.thread_spawn). Assert the emitted additionalContext carries the <runbook> block listed in hooks/context/subagent/agents/issue-planner.md.
- Negative test: a Codex subagent
SessionStart with no agent_type resolution records identity but emits no additional context.
- Regression: main-session Codex
SessionStart behavior unchanged — keep existing assertions.
Acceptance Criteria
Out of Scope
- Adding a true
SubagentStart event to Codex's manifest. Codex doesn't expose that event; we route through SessionStart's subagent branch instead.
- Backporting per-agent emission to historical Codex sessions — only new sessions fire
SessionStart.
- Heavier main-session authoring policy injection for subagents. The contract stays: launcher + authoring resolver + per-agent runbook, nothing more.
Related
Description
Make Codex emit per-agent SubagentStart context when a subagent's
SessionStartfires. Todayhook_codex.py::_handle_agent_session_startidentifies the subagent (via payload markers or transcriptsession_meta) and callsrecord_subagent_start, but intentionally skips additional-context emission — the module docstring says "Subagent sessions skip the main-session workflow authoring policy injection." That blanket skip was correct when no per-agent template existed; withissue-planner(#145) merged, Codex subagents now silently miss the<runbook>block and other per-agent context that Claude subagents inherit viaSubagentStart.The shared builder
build_subagent_start_context(config, plugin_root, runtime, agent_type)already supportsruntime="codex"and renders the codex launcher snippet — the infrastructure is in place. Only the Codex hook adapter declines to call it.Note: the contract here is not main-session authoring policy. It is the narrower per-agent slice — launcher + authoring resolver + per-agent runbook — that subagents need to operate. The Codex hook should inherit the same scope Claude's
SubagentStartinherits today, no more.Approach
_handle_agent_session_startinplugins/workflow/hooks/scripts/hook_codex.py:record_subagent_start, callbuild_subagent_start_context(config, _plugin_root(), runtime="codex", agent_type=<resolved name>).agent_typefrom transcript metadata via the existing_agent_name_from_metadatahelper.hookSpecificOutput(hookEventName: "SessionStart",additionalContext: <block>) when a non-empty block is produced.parent_thread_idcannot be resolved — no metadata, no agent block.hook_codex.pyso it no longer claims Codex subagents skip all injection; describe the new contract.plugins/workflow/hooks/AGENTS.md:build_subagent_start_context, but still not the heavier main-session authoring guidance.SessionStart's subagent branch.plugins/workflow/tests/test_workflow_hook.pyassert that Codex agent SessionStart emits noadditionalContext, flip the assertion: emit whenagent_typematches a known per-agent template; stay silent for unknown agents.issue-plannervia a synthesized Codexsession_metaand verifying the rendered block carries the expected<runbook>tag.SessionStartwith no resolvable agent name still records identity but emits no additional context.Affected Paths
plugins/workflow/hooks/scripts/hook_codex.pyplugins/workflow/hooks/AGENTS.mdplugins/workflow/tests/test_workflow_hook.pyUnit Test Strategy
build_subagent_start_contextfor Claude. Mirror the same shape for Codex with a fixturesession_metathat names the agent (agent_name: "issue-planner", or the equivalent slot undersource.subagent.thread_spawn). Assert the emittedadditionalContextcarries the<runbook>block listed inhooks/context/subagent/agents/issue-planner.md.SessionStartwith noagent_typeresolution records identity but emits no additional context.SessionStartbehavior unchanged — keep existing assertions.Acceptance Criteria
_handle_agent_session_startcallsbuild_subagent_start_context(..., runtime="codex", agent_type=<resolved>)and emitsadditionalContextviaemit_jsonwhen the block is non-empty._agent_name_from_metadata— no new metadata-walking code introduced.hooks/AGENTS.mdinvariant text reflects the new "subagents inherit per-agent context" contract.SessionStartmain-session tests still pass unchanged.SessionStartagainstissue-plannerasserts the rendered block carries the runbook intents listed inhooks/context/subagent/agents/issue-planner.md.additionalContextemission.pytest plugins/workflow/testspasses.Out of Scope
SubagentStartevent to Codex's manifest. Codex doesn't expose that event; we route throughSessionStart's subagent branch instead.SessionStart.Related
issue-planneragent introduced; the per-agent template that surfaces this gap.