{"record":{"id":"7a6c5b81b33e277e","repo":"tinyhumansai/openhuman","slug":"agent-parent-is-a-worker-tier-and-must-not-l","errorCode":null,"errorMessage":"agent `{parent}` is a `worker` tier and must not list `{child}` in its subagents — workers are leaf executors.","messagePattern":"agent `(.+?)` is a `worker` tier and must not list `(.+?)` in its subagents — workers are leaf executors\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/openhuman/agent/registry/agents/loader.rs","lineNumber":410,"sourceCode":"/// agents that violate the contract fail the boot rather than crashing\n/// at spawn time.\npub fn validate_tier_hierarchy(defs: &[AgentDefinition]) -> Result<()> {\n    let tier_by_id: HashMap<&str, AgentTier> =\n        defs.iter().map(|d| (d.id.as_str(), d.agent_tier)).collect();\n\n    for def in defs {\n        for entry in &def.subagents {\n            let child_id = match entry {\n                SubagentEntry::AgentId(id) => id.as_str(),\n                // Workflow wildcards always route to `integrations_agent`\n                // (a Worker) via a single collapsed delegation tool —\n                // not subject to the tier-mismatch rule.\n                SubagentEntry::Skills(_) => continue,\n            };\n\n            // Worker leaves: no open-ended spawn surface.\n            if def.agent_tier == AgentTier::Worker {\n                anyhow::bail!(\n                    \"agent `{parent}` is a `worker` tier and must not list `{child}` in its \\\n                     subagents — workers are leaf executors.\",\n                    parent = def.id,\n                    child = child_id,\n                );\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`","sourceCodeStart":392,"sourceCodeEnd":428,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/agent/registry/agents/loader.rs#L392-L428","documentation":"Tier-hierarchy validation in the builtin agent loader: an agent whose agent_tier is worker must not declare any AgentId subagents, because workers are leaf executors with no open-ended spawn surface. The check runs over all loaded definitions including builtins; Skills wildcard entries are exempt since they collapse into a single delegation tool, and unknown child ids are deliberately not masked as tier errors (that is a separate subagents integrity concern).","triggerScenarios":"A custom agent TOML sets agent_tier = \"worker\" while also listing subagents entries; a builtin's TOML is edited locally to add delegation to a worker-tier agent.","commonSituations":"Copying an orchestrator-style definition and only changing the tier to worker; authors assuming any agent may delegate.","solutions":["Remove the subagents list from the worker definition, or","change its agent_tier to a tier that is allowed to delegate.","Reload definitions afterwards to confirm the registry validates cleanly."],"exampleFix":"# before\nid = \"my-worker\"\nagent_tier = \"worker\"\n[[subagents]]\nid = \"integrations_agent\"\n\n# after (option A: leaf worker)\nid = \"my-worker\"\nagent_tier = \"worker\"\n# (no subagents)\n\n# after (option B: delegating tier)\nid = \"my-worker\"\nagent_tier = \"standard\"","handlingStrategy":"validation","validationCode":"fn workers_have_no_agent_subagents(defs: &[AgentDefinition]) -> anyhow::Result<()> {\n    for def in defs {\n        if def.agent_tier == AgentTier::Worker\n            && def.subagents.iter().any(|s| matches!(s, SubagentEntry::AgentId(_)))\n        {\n            anyhow::bail!(\"worker `{}` lists subagents\", def.id);\n        }\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run the same tier-hierarchy lint over custom agent TOMLs at authoring time, not just at loader startup.","When copying an orchestrator template, strip its subagents if you downgrade the tier to worker.","Remember Skills wildcard entries are exempt - only explicit AgentId entries violate the leaf rule."],"tags":["agent","tier","validation","registry","config"],"backgroundTag":"invalid-config-value","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}