{"record":{"id":"7216bffc4324da7e","repo":"tinyhumansai/openhuman","slug":"agent-parent-ptier-lists-child-ctier","errorCode":null,"errorMessage":"agent `{parent}` ({ptier}) lists `{child}` ({ctier}) in subagents — {reason}","messagePattern":"agent `(.+?)` \\((.+?)\\) lists `(.+?)` \\((.+?)\\) in subagents — (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/openhuman/agent/registry/agents/loader.rs","lineNumber":433,"sourceCode":"                );\n            }\n\n            let Some(child_tier) = tier_by_id.get(child_id).copied() else {\n                // Unknown id — that's a separate `subagents` integrity\n                // concern (covered by existing tests / runtime spawn\n                // resolution); don't mask it as a tier error.\n                continue;\n            };\n\n            // Same-tier delegation is forbidden for chat and reasoning.\n            // (Chat→Chat would defeat the whole point of the fast tier;\n            // Reasoning→Reasoning produces a depth-blowing recursion of\n            // slow models.) The pair-rule lives in `validate_tier_transition`\n            // (the single source of truth shared with the runtime spawn gate\n            // in `run_subagent`); here we wrap its reason with the offending\n            // agent ids + tiers for a boot-time-friendly diagnostic.\n            if let Err(reason) = validate_tier_transition(def.agent_tier, child_tier) {\n                anyhow::bail!(\n                    \"agent `{parent}` ({ptier}) lists `{child}` ({ctier}) in subagents — {reason}\",\n                    parent = def.id,\n                    ptier = def.agent_tier.as_str(),\n                    child = child_id,\n                    ctier = child_tier.as_str(),\n                );\n            }\n        }\n    }\n\n    Ok(())\n}\n\n/// Parse a single [`BuiltinAgent`] triple into a finished [`AgentDefinition`].\nfn parse_builtin(b: &BuiltinAgent) -> Result<AgentDefinition> {\n    // The TOML ships without `system_prompt` — serde falls back to\n    // `defaults::empty_inline_prompt` — and the loader injects the\n    // rendered sibling `prompt.md` immediately below.","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/src/openhuman/agent/registry/agents/loader.rs#L415-L451","documentation":"Same boot-time validator (loader.rs:433), wrapping a validate_tier_transition failure: the parent's tier may not delegate to the child's tier. Forbidden pairs are Chat→Chat (would defeat the whole point of the fast tier) and Reasoning→Reasoning (depth-blowing recursion of slow models). The pair-rule is the single source of truth shared with the runtime spawn gate in run_subagent, so the definition is rejected at boot with the offending ids and tiers spelled out.","triggerScenarios":"A chat-tier agent listing another chat-tier agent in subagents; a reasoning-tier agent listing another reasoning-tier agent; adding a new agent to a team and wiring it under a same-tier peer.","commonSituations":"Building multi-agent teams in TOML with every agent defaulted to the same tier; refactoring agent tiers without re-checking the subagents graph.","solutions":["Change the child's agent_tier (e.g. make the inner executor a worker) so the transition is allowed","Remove the subagents entry if the delegation is not needed","Move the delegation up a level: let an orchestrator/reasoning parent own both agents instead of chaining same-tier peers","Re-run registry load to confirm a clean boot"],"exampleFix":"# before — both tiers are \"reasoning\"\n[agent \"reviewer\"]\nsubagents = [\"second_reviewer\"]\n\n# after — inner agent becomes a worker leaf\n[agent \"reviewer\"]\nsubagents = []   # or set second_reviewer: agent_tier = \"worker\"","handlingStrategy":"validation","validationCode":"// Same validator catches both worker-leaf and tier-transition violations pre-boot:\nopenhuman::agent::registry::agents::validate_tier_hierarchy(&defs)?;","typeGuard":"// Allowed delegation pairs, mirroring validate_tier_transition\nfn transition_ok(parent: AgentTier, child: AgentTier) -> bool {\n    !matches!((parent, child),\n        (AgentTier::Chat, AgentTier::Chat) |\n        (AgentTier::Reasoning, AgentTier::Reasoning))\n}","tryCatchPattern":"// On boot failure, parse out parent/child ids from the message to point at the TOML\nif let Err(e) = registry.load() {\n    let msg = e.to_string();\n    if msg.contains(\"in subagents\") {\n        eprintln!(\"tier-hierarchy violation: {msg}\\nChange the child's tier or remove the entry.\");\n    }\n    return Err(e);\n}","preventionTips":["Design teams top-down: orchestrator/reasoning parents over worker leaves","Never chain two same-tier (chat-chat, reasoning-reasoning) agents","Re-run validate_tier_hierarchy whenever tiers change in any agent TOML"],"tags":["rust","agents","config","boot","tier-hierarchy"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}