feat(flow): a decision-table node puts rule evaluation into the graph - #3329
Merged
Conversation
added 2 commits
September 2, 2026 10:52
openregister.decision-table evaluates an inline DMN-style table against each item through the shared evaluator and writes the outputs onto configured item fields. The table travels in the node config, so flow versioning, pinning and x-openregister-flows import apply to the rules with no extra machinery. A table the evaluator cannot execute is refused when the flow is saved: DecisionTableValidator probes every rule cell through the evaluator's own grammar, so the accepted grammar is the executable one by construction. No-match is an explicit choice, a complete default row or a loud failure. The node never suspends: rule steps are the engine's, human decisions stay with the user task. The unit fixture is dossiq's real LHS enforcement matrix driven through dossiq's own inputMapping/outputMapping vocabulary, proving the migration that retires the app-side copy cannot strand.
…nd settle the analyzers The listener test constructs every built-in by name, so the new node is a 25th argument there; the two StaticAccess findings are suppressed with their reasons, and the long form-help strings are wrapped.
Contributor
Quality Report — ConductionNL/openregister @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| test-l10n-parity | ✅ | ||||
| format | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| check-l10n-js | ✅ | ||||
| composer | ✅ | ✅ 174/174 | |||
| npm | ✅ | ✅ 543/543 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ⏭️ deferred — runs on the promotion into beta/main, not on a pull request into development | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-09-02 09:21 UTC
Download the full PDF report from the workflow artifacts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
OpenSpec change
flow-decision-tables: a new node typeopenregister.decision-tableputs rule evaluation into the flow graph. It evaluates a DMN-style decision table against each item through the shared evaluator fromshared-decision-table-evaluatorand writes the outputs onto configured item fields. dossiq is staging the retirement of its app-side decision tables against this node, per the directive that apps get no flow engines of their own.What changed
lib/Service/Flow/Nodes/DecisionTableNode.php: the node. ImplementsIFlowNode,IFlowNodeConfigKeysandIFlowNodeConfigForm. Config keys:table(inline definition),inputMapping,outputMapping(name to dotted path, same-name default, dossiq's handler vocabulary kept verbatim),defaultOutputs(explicit no-match row),resultKey(optional evaluation record). Deterministic, per item, never suspends.lib/Service/Dmn/DecisionTableValidator.php: save-time validation that probes every rule cell throughUnaryTestEvaluator::matches(), so the accepted grammar is the executable grammar by construction. Structural refusals: unimplemented hit policy by name, empty inputs/outputs/rules, duplicate column names, positional count mismatches, non-integer priority.lib/Service/Dmn/DecisionTableEvaluator.php: exposeseffectiveType()and makesIMPLEMENTED_HIT_POLICIESpublic so validator and evaluator cannot drift. No behaviour change; the existing evaluator suite stays green.lib/Listener/FlowNodeRegistrationListener.php: registers the node as the 25th built-in.openspec/changes/flow-decision-tables/: proposal, design, tasks and spec. Design D-1 argues the storage decision: the table travels inline in the node config, so flow versioning, pinning andx-openregister-flowsimport apply with no extra machinery; a live object reference is refused there with reasons.Hit policies
All five the shared evaluator implements: UNIQUE, FIRST, PRIORITY, ANY, COLLECT. That is a superset of what dossiq's tables use. Anything else is refused at save, by name.
No-match behaviour
A complete
defaultOutputsrow decides the default and flagsdefaulted: truein the evaluation record; without one the step fails loudly withno_rule_matchedand the step'sonErrorpolicy decides. A partial default row and a default row on COLLECT are refused at save.Tests
78 decision-table assertions green, including a fixture translated from dossiq's real LHS enforcement matrix driven through dossiq's own
inputMapping/outputMappingvocabulary, per-policy evaluation, no-match both ways, determinism, and every validation refusal. Full local Unit slice run; the only new failures were the listener test arity, fixed here.Checks
--scope-to-diff: all diff-scoped gates pass. Gates 22 and 53 fail on development HEAD as well: hydra-gates v1.10.0's vendored manifest schema does not know theflowpage type that nc-vue 2.27.0 defines andsrc/manifest.jsonpage 18 uses. That fix belongs in ConductionNL/.github (vendored schema bump), not here.For the dossiq migration
Replace an
evaluateDecisiontransition action with anopenregister.decision-tablestep: put the table definition undertable, carry the existinginputMapping/outputMappingover unchanged, and adddefaultOutputsonly where a no-match should not fail the step. Nothing app-side needs to be declared or registered.