{"record":{"id":"8c576ba81b1a47b7","repo":"unicity-aos/aos-ce","slug":"publish-is-empty-or-missing-the-capsule-cannot-publish-any","errorCode":null,"errorMessage":"[publish] is empty or missing — the capsule cannot publish any event.","messagePattern":"\\[publish\\] is empty or missing — the capsule cannot publish any event\\.","errorType":"validation","errorClass":"Finding","httpStatus":null,"severity":"warning","filePath":"capsules/capsule-forge/src/checks.rs","lineNumber":198,"sourceCode":"        return;\n    };\n    for (key, val) in env {\n        if val.get(\"scope\").and_then(Toml::as_str) == Some(\"shared\") {\n            out.push(Finding::warn(\n                format!(\"[env].{key} sets scope = \\\"shared\\\", which is silently ignored.\"),\n                \"Remove `scope`; shared/operator-only env scope is not honoured from the manifest.\",\n            ));\n        }\n    }\n}\n\n/// Collect the `[publish]` and `[subscribe]` topic keys, warning if a table is\n/// empty (fail-closed: the capsule then can't publish/subscribe at all).\nfn collect_topics(root: &Toml, out: &mut Vec<Finding>) -> (Vec<String>, Vec<String>) {\n    let pub_keys = table_keys(root, \"publish\");\n    let sub_keys = table_keys(root, \"subscribe\");\n    if pub_keys.is_empty() {\n        out.push(Finding::warn(\n            \"[publish] is empty or missing — the capsule cannot publish any event.\",\n            \"Add the tool-bus publish keys `tool.v1.execute.*.result` and `tool.v1.response.describe.*`.\",\n        ));\n    }\n    if sub_keys.is_empty() {\n        out.push(Finding::warn(\n            \"[subscribe] is empty or missing — the capsule cannot receive any event.\",\n            \"Add one `tool.v1.execute.<tool>` per tool plus `tool.v1.request.describe`.\",\n        ));\n    }\n    (pub_keys, sub_keys)\n}\n\nfn table_keys(root: &Toml, table: &str) -> Vec<String> {\n    root.get(table)\n        .and_then(Toml::as_table)\n        .map(|t| t.keys().cloned().collect())\n        .unwrap_or_default()","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/unicity-aos/aos-ce/blob/f6f22024fb1e8d122f28a1b4a9f75aee448ae839/capsules/capsule-forge/src/checks.rs#L180-L216","documentation":"collect_topics gathers keys from the manifest's [publish] table and warns when it is empty or absent. Publishing is fail-closed: a capsule with no declared publish topics cannot publish any event on the bus. Forge warns because the standard tool-bus capsules are expected to publish tool results and describe responses.","triggerScenarios":"validate_manifest -> collect_topics finds table_keys(root, \"publish\") empty — either the [publish] section is missing from the manifest or it contains no topic keys.","commonSituations":"New capsule scaffolded with only a [subscribe] section; author deleting publish entries after refactoring; template manifest for a purely-subscribing capsule reused for a tool capsule that must reply on the tool bus.","solutions":["Add a [publish] section with the tool-bus keys `tool.v1.execute.*.result` and `tool.v1.response.describe.*`.","If the capsule genuinely publishes nothing, acknowledge the warning is intentional and leave it (behavior is fail-closed by design).","Re-run forge validate after adding topics to confirm the warning clears."],"exampleFix":"# before\n[subscribe]\n\"tool.v1.execute.echo\" = {}\n# after\n[subscribe]\n\"tool.v1.execute.echo\" = {}\n\n[publish]\n\"tool.v1.execute.*.result\" = {}\n\"tool.v1.response.describe.*\" = {}","handlingStrategy":"validation","validationCode":"# ensure [publish] exists with required keys before shipping\ntomlq '.publish | keys' capsule.toml | grep -q 'tool.v1.execute.*.result' || echo \"missing publish topics\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Start tool capsules from the forge template that includes [publish]","Never delete publish keys during refactors without re-validating","Run forge validate in CI on every manifest"],"tags":["manifest","publish","topics","validation"],"backgroundTag":"empty-required-field","analyzedSha":"f6f22024fb1e8d122f28a1b4a9f75aee448ae839","analyzedAt":"2026-09-13T03:04:44.565Z","contentChangedAt":"2026-09-13T03:04:44.565Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}