Skip to content

feat(decision-types): make the decisionType vocabulary configuration, not code - #1099

Merged
rubenvdlinde merged 1 commit into
developmentfrom
feature/decision-types-as-configuration
Sep 2, 2026
Merged

feat(decision-types): make the decisionType vocabulary configuration, not code#1099
rubenvdlinde merged 1 commit into
developmentfrom
feature/decision-types-as-configuration

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Why

A decision type is configuration on the existing decision abstraction, not code. Today the vocabulary is a closed list mirrored in four homes: the DecisionIntegrationService::ALLOWED_TYPES constant, the Decision enum in decidesk_register.json, its copy in decidiq_mock_register.json, and the DecisionTemplate narrowing in register.d/68. A parity test pins the four together. Adding one type costs a release in four places. That is how dossiq's advice stalled, and how the pending woo-decision need stalls now.

What changes

  • One authority. The vocabulary lives in the decision_types app setting. DecisionTypeRegistry reads it; the integration hub validates against it and fails closed on an unknown type. The refusal message names the fix: an administrator adds the type, no release.
  • Seed. SeedDecisionTypes (registered in info.xml under install and post-migration) writes today's vocabulary once, woo-decision included. It never overwrites a stored row, so admin edits survive every upgrade. Until it runs, the registry falls back to the same shipped list, so nothing fails closed in the gap.
  • Schema enums drop. The three decisionType declarations become free-text strings with referential validation. No enum is generated either: a copy that drifts from the store recreates the four-homes problem. The trade-off (raw OpenRegister object writes lose enum checking) is documented on the schema; that path already bypasses the chair, quorum and terminal-completeness gates.
  • Parity test inverted. It now proves no ALLOWED_TYPES constant exists, no schema home carries an enum, and the seed covers every type a fleet caller sends (dossiq: contract-renewal, report-adoption, advice, bezwaar-decision, woo-decision; stackiq: contract, contract-renewal).
  • No enum literal added anywhere. woo-decision enters as seed data.
  • OpenSpec change decision-types-as-configuration with spec delta on decidesk-contract-decision-hub (new REQ-DCDH-009, modified REQ-DCDH-001).
  • conduction/hydra-gates bumped to v1.11.0.

Adding a type after this PR

occ config:app:get decidiq decision_types
occ config:app:set decidiq decision_types --type array --value '["motion", ..., "subsidy-award"]'

Follow-up (named in design.md)

Per-type behavioural configuration (motion and amendment branching, kind grouping in decisionLink.js, lifecycle domain defaults) belongs to the ADR-037 consumer rewrite. The string registry stays the validity authority.

Verification

  • lint, phpcs, phpmd (per subdir, both rulesets), psalm, phpstan: all clean, run individually in the foreground.
  • PHPUnit: 1326 tests, 0 failures (30 pre-existing skips).
  • Hydra gates, full scope: 75 of 75 applicable gates ran, 0 FAIL.
  • Mutation check on the unknown-type refusal: an inverted guard kills 11 tests; a registry that ignores the store kills 5.
  • l10n: test:l10n, check:l10n-js, check:manifest, check:nav-ceiling, format all pass; the schema-l10n ratchet baseline drops 1632 to 1613 (removed enum strings).

Note: test:l10n:parity fails on a missing l10n/rm.json. Pre-existing, not run in CI (per the workflow comment), and this PR adds no translatable strings.

… not code

The vocabulary lived in four homes: the ALLOWED_TYPES constant, the
Decision enum in both registers, and the DecisionTemplate narrowing in
fragment 68, pinned together by a parity test. Adding a type cost a
release in four places, which is how dossiq's advice and the pending
woo-decision need each stalled.

One authority now: the decision_types app setting. SeedDecisionTypes
writes today's vocabulary (woo-decision included) once and never
overwrites an admin's edits. DecisionTypeRegistry validates
referentially, falling back to the seed only while no row is stored.
The integration hub fails closed on an unknown type and its refusal
names the fix: an administrator adds the type, no release.

The schema declarations drop their enums on purpose: a declared enum
drifts from the store and recreates the four-homes problem. The parity
test inverts, proving no second authority exists and that the seed
covers every type a fleet caller sends (dossiq, stackiq).

Also bumps conduction/hydra-gates to v1.11.0.
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/decidiq @ f96e206

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
check-nav-ceiling
test-l10n
format
check-l10n-js
check-schema-l10n
composer ✅ 104/104
npm ✅ 537/537
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 10:45 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde
rubenvdlinde merged commit d213cae into development Sep 2, 2026
49 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