Skip to content

feat(dmn): named decision tables are stored once and flows reference them by key - #4572

Merged
rubenvdlinde merged 9 commits into
developmentfrom
feat/stored-decision-tables
Oct 12, 2026
Merged

rubenvdlinde merged 9 commits into
developmentfrom
feat/stored-decision-tables

Conversation

@rubenvdlinde

@rubenvdlinde rubenvdlinde commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

What

Named decision tables, stored once in OpenRegister, used by reference (decision 186, ADR-118 capability C15).

  • Core register decision-tables, schema decision-table (key, name, description, hitPolicy, inputs, outputs, rules, owner): lib/Settings/decision_table_register.json, imported by the new repair step ImportDecisionTableRegister (info.xml version moved so it runs).
  • DecisionTableValidationListener refuses, at save, a stored table the evaluator cannot run (the same DecisionTableValidator the inline step uses) and a second table under a key another table carries (decision-table-invalid).
  • DecisionTableStore resolves a table by key, else by uuid, re-validates it on read, and has evaluate(reference, inputs) for an app that decides outside a flow. An unknown reference is refused, never answered with an empty table.
  • The openregister.decision-table step takes tableRef as an alternative to table. Both, or neither, is refused at save. A tableRef that names nothing executable is refused at save and fails the step at run.

Why

Q-dossiq-L6-4, answered by Ruben as decision 186: OpenRegister's step carried its table inline and had no store, so dossiq kept its own (decisionTable schema, CRUD, five routes, an admin tab, a translator that inlines stored tables, and the VTH enforcement recommendation reading a stored table). Those move onto this capability in a dossiq PR that follows (lane R3).

Change: openspec/changes/stored-decision-tables/ (REQ-SDT-01..04). Task 1.5 (live: import, store, run by tableRef) is owed to the live pass; 1.6 (point the editor at the register) is a follow-up.

Verified

  • New tests, neutral fixture (a parcel-carrier table): DecisionTableStoreTest, DecisionTableValidationListenerTest, DecisionTableNodeStoredTableTest. Red on development: the store and listener classes do not exist and the step has no tableRef. Green: tests/Unit/Service/Dmn, the listener test and tests/Unit/Service/Flow/Nodes/ 119 tests OK, exit 0; tests/Unit/Settings, tests/Unit/Repair, tests/Unit/AppInfo 205 tests OK, exit 0.
  • phpcs, phpmd (with baseline) and phpstan on the changed classes: exit 0. The one phpcs warning (Application has no @spec) is inherited.
  • Not run locally: the full composer check:strict (the check slots were held by other lanes). CI is the full check for this PR.

Decision 196: lands together with buildiq#1100 and .github#840

Ruben decided that OpenRegister owns the schema slug decision-table, and that buildiq's rule engine moves onto these stored tables. The slug stays decision-table.

  • Gate 106 on this PR fails today (declares schema slug 'decision-table', which belongs to 'buildiq'). chore(gate-106): OpenRegister owns the schema slug decision-table (decision 196) .github#840 moves the owner in fleet-schema-slugs.json to openregister. With that contract the checker reports 0 finding(s) on this tree. CI reads the contract from main, so .github#840 lands first.
  • refactor(rules): decision tables live in OpenRegister's decision-tables register (decision 196) buildiq#1100 removes buildiq's own decision-table schema and moves its tables into this register with a repair step. Land the three together.
  • Added in 6a89b48: DecisionTableValidationListener now validates a row only when it is in the decision-tables register. Before, it matched the schema slug alone, so buildiq's still-existing schema of the same slug would have had its rows refused. Red: 2 failing tests against the old listener. Green: 27 targeted tests.
  • ImportDecisionTableRegister now has unit tests. CI's coverage guard had read its 137 added statements as untested (86.04% vs 90.19%).
  • The one PHPUnit failure in CI, ObjectServiceRunAsAnonymousTest::testTheSubjectIsGoneEvenWhileThePhpSessionStillNamesAUser, passes locally (13 tests OK). This PR touches neither that test nor ObjectService. Read it as inherited or flaky until CI says otherwise.

…them by key

Decision 186: a reusable decision table lives in OpenRegister as a generic
capability (ADR-118 C15). A core decision-tables register, refused at save
when the evaluator cannot run a table or its key is taken; DecisionTableStore
resolves a table by key or uuid and evaluates it for apps; the decision-table
flow step takes tableRef as an alternative to an inline table.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openregister @ 827876c

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 ❌
composer ✅ ✅ 173/173
npm ✅ ✅ 648/648
app:check-code ⏭️
info.xml ✅
REUSE ✅
lockfile sync ✅
PHPUnit ❌ 25341/25341 · 25 skipped (no test failed, the job failed for another reason — see its log)
Newman ✅ 635/635
Playwright ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development.
Hydra gates ❌

Quality workflow — 2026-10-10 21:44 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openregister @ 046e901

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 ✅
composer ✅ ✅ 173/173
npm ✅ ✅ 648/648
app:check-code ⏭️
info.xml ✅
REUSE ✅
lockfile sync ✅
PHPUnit ❌ 25374/25374 · 25 skipped (no test failed, the job failed for another reason — see its log)
Newman ✅ 635/635
Playwright ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development.
Hydra gates ❌

Quality workflow — 2026-10-11 07:56 UTC

Download the full PDF report from the workflow artifacts.

…e register import

The listener matched a row by schema slug alone, so another app's own
decision-table schema (buildiq's, until it moves onto this register) would
have had its rows refused. It now also requires the decision-tables register.
ImportDecisionTableRegister gains its unit tests (CI's coverage guard read the
added statements as untested).
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openregister @ 7de6ad3

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 ✅
composer ✅ ✅ 173/173
npm ✅ ✅ 648/648
app:check-code ⏭️
info.xml ✅
REUSE ✅
lockfile sync ✅
PHPUnit ✅ 25594/25594 · 25 skipped
Newman ✅ 635/635
Playwright ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development.
Hydra gates ❌

Quality workflow — 2026-10-11 21:28 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde
rubenvdlinde merged commit 58a2f16 into development Oct 12, 2026
45 of 48 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant