Mirrors the enabled_skills plumbing per plans/custom-commands-design.md §5:
- global: Config + AppConfig structs, from_config copy, and the
COYOTE_ENABLED_MACROS env-override arm (csv_to_vec parsing)
- role: frontmatter via parse_string_or_array (list or csv string),
plus the export() mirror so Role::save round-trips the field
- agent (non-graph): plain serde on AgentConfig; graph.yaml silently
ignores the key (pinned by test, no field on Graph by design)
- session: plain serde with csv-or-vec deserializer
Empty list/string deserializes to Some([]) (explicit zero), distinct
from absent/null (None) — pinned by tests at every level, including
the env arm (serial-fenced against the from_config tests, which read
the process env via load_envs).
`RagNode` gains an optional `driver`, forwarded into `RagInitConfig` so a
graph node can build its knowledge base on duckdb instead of yaml. Nodes
that name no driver forward `None`, which still resolves to yaml, so
existing workflows are unaffected.
An unknown driver is rejected up front rather than at construction time.
`Rag::create` dispatches unknown drivers to its yaml catch-all, so a typo
would otherwise embed every document and persist the bogus string, after
which every subsequent load fails validation and the agent cannot start.
The check asks `RagData::validate()` through a probe value instead of
restating the list of valid drivers, so the two cannot drift.