Problem/Motivation
A node's configuration schema names eight keys one at a time, and each one is the private shape of a node feature: assignments, status, timeout, timers and retry, which this module provides, and interaction, park_notify and notify_to, which orchestra_interaction, orchestra_interaction_notification and orchestra_notification provide.
Three of those eight therefore describe an optional module's data in the kernel's own file, and nothing on the page says which three. The kernel already knows better everywhere else: a node feature declares the key it owns through getConfigKey(), and NodeFeatureManager maps that key back to the providing module, which is how a workflow records its module dependencies. The schema is the one place the kernel still writes those keys out by hand.
The cost is paid by whoever comes next. A module adding a node feature has to patch this module's schema for its own key, or its settings fail validation on a workflow that carries them, because the workflow schema is FullyValidatable. That is the opposite of how every other plugin family here works: a module already declares orchestra.interaction.entity_form or orchestra.audience.everyone for its own plugins, in its own file, and the kernel only types the container.
Proposed resolution
Give node feature settings one container, typed by the feature that owns each entry, so the kernel declares the container once and every feature declares its own shape.
features: type: sequence label: 'Node feature settings' sequence: type: orchestra.node_feature.[%key]
Each feature's module then declares orchestra.node_feature.timers, orchestra.node_feature.interaction and so on beside the plugin, the way it already declares that plugin's other schema. A ninth feature from a new module needs no change here at all, which is the whole point: that is the one thing none of the cheaper answers below gives.
A wildcard entry, orchestra.node_feature.*, goes with it. The workflow schema is FullyValidatable, so without a fallback a feature whose module declares no entry would make every workflow carrying it invalid. That is arguably the right pressure on a new feature, but it would be applied first to features that already exist and were written when nothing asked them for it, which is the wrong moment to apply it. It has to be typed ignore rather than mapping: a bare mapping types no children, so an undeclared feature carrying any data at all would still be refused.
This is the shape core uses for the same problem twice over. Views types a display's handlers by their plugin id and lets each module declare views.filter.* or views.argument.* for its own; core types third party settings as a sequence keyed by module name, [%key] resolving to the owning module's type. Both keep the container with the owner of the structure and the entry with the owner of the data.
The question this left open, settled
A new features key, not the eight moved to the top of the node with a marker. The node keeps a strict mapping over its own six keys, so a typo in label is still refused and only the feature area is open. Making the whole node a sequence would open the kernel's own keys too and lose that check, which erases the boundary rather than drawing it.
Remaining tasks
None.
Alternatives considered
hook_config_schema_info_alter() would let each module add its own key to the node mapping, leaving the node's keys where they are. It is rejected because the workflow schema is FullyValidatable, so every workflow's validity would then depend on that alter having run, for a gain that is only tidiness; and because it is a mechanism core documents and does not itself use, while the container-and-entry shape above is the one core actually ships.
Leaving the eight keys where they are, with a comment at each naming the module that owns it and a test that refuses a ninth added silently, is the smallest option. It does not remove the need for a new module to patch this one, which is the whole complaint.
AI-Generated: Yes (Claude Code was used to help draft this issue summary and to write the code and tests on the merge request. I reviewed and ran the work myself before posting it.)
Issue fork orchestra-3622548
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
mably commentedComment #4
mably commentedComment #5
mably commentedComment #7
mably commented