refactor(softwarecatalog): translate Dutch vocabulary to English - #506
Conversation
…ending the existing migration
Applies the fleet Dutch->English pass: 93 property names plus identifiers,
comments and docblock shapes. Extends RenameDutchCatalogColumns (6 pairs -> 84)
rather than adding a second step.
THE PIPELINE ABORTED BEFORE TOUCHING ANYTHING, WHICH IS THE POINT
`relation` already has BOTH `bron` and `source`, and their descriptions say the
same thing — a pre-existing duplicate from a partial earlier rename. Renaming
`bron` onto `source` would collide and lose data; inventing a third name would
be worse. Consolidating them is a DATA decision for the owners, so `bron` is
excluded with that reason recorded in .exclude-swc-tr.json.
TWO MORE EXCLUSIONS, FOR FLEET CONSISTENCY. This app already SHIPPED
`beschrijving_kort -> short_description` and `beschrijving_lang -> description`.
My dictionary would have produced `descriptionKort` and `descriptionLang` — HALF
DUTCH, and forking the app against its own migration. It got as far as the
registers (31 occurrences) before I noticed, because `kort` and `lang` carry
none of the Dutch orthographic markers the residual guard looks for. Guard
widened; both names excluded; branch rebuilt from clean.
`versies` was the same story — `standaardVersies` became `standardVersies`.
Added to the dictionary, guard widened again.
TWO DEFECTS I CAUSED BY HAND, BOTH BYPASSING THE TOOL'S GUARDS
- shortening `$moduleVersionSchemaId` with a plain regex renamed the parameter
but not its NAMED ARGUMENTS, so three call sites broke. The AST tool moves
both together; my regex did not.
- the `name` alias block in DataMapper normalised the incoming key `name` onto
the then-canonical `naam`. The canonical IS `name` now, so the block became
`isset($data['name']) === false && isset($data['name']) === true` — a
contradiction that can never run. Removed rather than left as dead code.
VERIFIED in the container against a CONTROL run of clean development:
phpstan [OK] · psalm 0 no-cache (baseline 0) · phpmd 0 (baseline 0) · phpcs 4
errors, IDENTICAL to the baseline.
NOT VERIFIED LOCALLY: the test suite. softwarecatalog's bootstrap loads every
installed app, and `nldesign` in this container is missing its vendor — the run
dies before reaching a test, on the baseline too. CI must confirm the suite.
…ecs, and a filter VALUE phpstan passed locally and failed in CI. Cause: I "fixed" phpcs by changing /** @var */ to /* @var */, and a SINGLE-ASTERISK comment is not a docblock, so phpstan ignores it entirely. The annotation was dead the moment it satisfied the other tool. Restructured instead: read every value BEFORE the write that narrows the inferred array shape, which needs no annotation and cannot rot. FRONTEND SPECS were stale rather than wrong: they pass `standaard` as an UNQUOTED object key (`{ standaard: [...] }`), which the quoted-string rename cannot see. The register calls it `standard`, so the specs were simply behind. AND ONE REAL DEFECT THE MANIFEST WAS ABOUT TO SHIP "filter": { "gemmaType": "standaard" } -> "standard" `gemmaType` is a STORED DATA VALUE on GEMMA element objects, not a property name. Renaming it makes the filter match nothing and the Standaarden list page silently goes empty — no error, no test, just an empty page. Found by diffing every filter VALUE against the baseline rather than reading the diff. That sweep now also covers lib/Settings: three filter values moved in total, and only this one was data — the other two are `@objectId` / `@object.name` tokens that did not exist in the baseline at all. phpstan [OK] and phpcs at its baseline of 4 with both fixes in place together, which was the thing that failed before.
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 718/718 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-13 10:23 UTC
Download the full PDF report from the workflow artifacts.
…ted method
PHPUnit failed on ReflectionException for 28 private methods. The AST renamer
correctly renamed them and their `$this->` call sites, but the tests invoke them
BY NAME as a string:
new ReflectionMethod(Service::class, 'normaliseCurrentStandaarden')
A string is invisible to the AST, and method names are not in the property map,
so nothing moved it. Fixed by diffing private-method declarations against the
baseline and updating every test that names an old one — 5 files.
That diff also exposed a HALF-TRANSLATION the guard let through:
`updateGeregistreerdDoor` had become `updateGeregistreerdBy` — `door` mapped to
`by` inside a compound whose other half stayed Dutch. `geregistreerd` was not in
the dictionary and carries none of the markers the residual check looks for
('ee' followed by n or s; this is 'eer'). Token added, guard widened for `-eerd`
endings, and the name is now `updateRegisteredBy`.
phpstan [OK], phpcs at its baseline of 4.
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 718/718 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-13 10:28 UTC
Download the full PDF report from the workflow artifacts.
… fixtures
RenameDutchCatalogColumnsTest exercises `renameIsSafe()` with LITERAL pairs —
`('naam', 'name', [...declared...])` — to prove the migration only moves data
when the register has actually moved. Those literals are FIXTURE DATA, not app
vocabulary.
The blanket rename turned every `'naam'` into `'name'`, collapsing each scenario
into `('name', 'name', ...)`. The test then asserted TRUE and FALSE about
identical inputs, which is why it failed — and had it not failed, it would have
been worse: a migration-safety test that no longer distinguishes the safe case
from the data-loss case, still reporting green.
Restored from development verbatim. Its private-method references were checked
against the AST renames and needed none.
This is the fixture form of the rule the rest of this work follows: a Dutch
string that is DATA — a CSV header, a stored enum value, a config key, a test
input proving behaviour about Dutch names — does not move with the vocabulary.
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 718/718 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-13 10:34 UTC
Download the full PDF report from the workflow artifacts.
ReviewService returned 'waardering must be between 1 and 10' — half Dutch, half English, and the test asserted on the substring the rename had already moved. A user-facing MESSAGE is app text, not a wire contract: it is displayed, not parsed by anything. So it translates, unlike the CSV headers, ZGW resource keys and stored enum values elsewhere in this branch that were deliberately held.
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 718/718 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-13 10:43 UTC
Download the full PDF report from the workflow artifacts.
The wire dimension list between the Vue store and FacetController is ['referentiecomponent', 'standaard', 'applicatieservice', 'domein']. My rename translated exactly ONE of the four, leaving a half-Dutch vocabulary and breaking the round-trip the store spec asserts. These are not schema fields — facets.js documents that explicitly: 'the module/dienst schema has no field named referentiecomponent/standaard/domein/ applicatieservice (the real fields are referentieComponenten, standaardVersies)'. They are DERIVED dimension names on the wire, so they stay as a set or move as a set, and moving them is a client+server+URL-key change that is not this branch's job. Restored across FacetController, services/facets.js, both specs and the _gf_ URL keys.
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 718/718 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-13 10:59 UTC
Download the full PDF report from the workflow artifacts.
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 718/718 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-13 11:26 UTC
Download the full PDF report from the workflow artifacts.
… wrong dimension `referentieComponenten` had become `referenceComponenten` — `referentie` translated, `Componenten` did not — and it SHIPPED into the register, so FacetService read a field that no longer exists and array_column() got null. `componenten` was not in the dictionary and carries none of the markers the residual guard looks for. Token added, guard widened, and the field is now `referenceComponents` across the register, the code and the migration. The other failure was mine from the previous commit: restoring the wire dimension name to `standaard` left FacetServiceTest still reading `$result['standard']`. The dimension is the result KEY, so the test had to move back with it. This is the third half-compound this app produced (`kort`/`lang`, `versies`, now `componenten`). The guard is a heuristic over Dutch orthography and these words have none of it — each one is a dictionary gap, found by a failing test rather than by the guard. Worth saying plainly: the guard reduces the class, it does not close it. phpstan [OK], phpcs at its baseline of 4.
Restoring the wire dimension to `standaard` in FacetService::DIMENSIONS left the same key spelled `standard` in three other places: the result array the service builds, its @return array{} shape, and three assertions in FacetServiceTest. The dimension name IS the result key, so all four have to agree — the tests were reading a key nothing produced. Worth separating two things that look identical and are not: - the facet DIMENSION `standaard` is a wire name between the Vue store and FacetController, documented as NOT a schema field. It stays Dutch until the whole set of four moves together. - the register PROPERTY `standaard` IS a schema field and does move, which is why the migration still carries `'standaard' => 'standard'`. Both are correct at the same time; conflating them is what produced this churn. phpstan [OK].
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 718/718 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-13 19:06 UTC
Download the full PDF report from the workflow artifacts.
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 718/718 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-13 19:20 UTC
Download the full PDF report from the workflow artifacts.
…names Playwright failed where the merged-PR baseline passes it, so this was mine. The specs create catalog objects through the API with bodies keyed by PROPERTY names — `naam`, `beschrijving`, `waardering`, `versie` — which the registers no longer declare, so the seeds failed and the assertions after them cascaded. WHY THE PIPELINE MISSED THEM. Its rename rewrites QUOTED occurrences, which is right for PHP and JSON. TypeScript object literals use UNQUOTED keys (`waardering: 3`) and property access (`obj.naam`), and neither is quoted. 10 spec files updated across all three positions. Pairs were rebuilt from THIS BRANCH'S migration map rather than the shared .compose.json — that file is one path reused by every app's run, and reading it after another app had overwritten it is exactly what made the decidesk Newman check report a false clean. DELIBERATELY LEFT: 8 remaining matches are not property keys — `/standaarden` URL ROUTES (the manifest keeps them), a `standaard-detail` test label, local variables and comments. Renaming a route would break navigation for a cosmetic gain.
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 718/718 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-13 20:27 UTC
Download the full PDF report from the workflow artifacts.
THE PIPELINE HAD A HOLE AND THIS IS WHAT FELL THROUGH IT.
Its property pass rewrites QUOTED occurrences, which is right for PHP and JSON.
Javascript does not quote object keys, and property access has no quotes at all:
{ waardering: 3 } an object key POSTed to the API
obj.naam a read
"naam" the quoted form the old pass already handled
So Vue components kept posting and reading names the registers no longer
declare. This is a PRODUCTION defect, not a test artifact: on softwarecatalog
the review dialog never closed because the POST 400d silently, and the only
thing that noticed was an e2e assertion that a dialog should be hidden.
Fleet-wide it was 100 source files — procest 57, softwarecatalog 37, pipelinq 6.
decidesk had none, which is why it went green first time and hid the class.
Fixed by a new pass (rename-frontend.js) covering unquoted keys, property access
and quoted forms. Its pairs come from THIS repo diff of lib/Settings crossed
with the app own migration map — never from the shared .compose.json, which is
one path reused by every app run and was stale enough to make an earlier check
report a false clean.
Frontend files only; no PHP touched, so the PHP gates are unaffected. `node
--check` clean on every changed .js/.ts.
Two consequences of the frontend pass, both mechanical:
- prettier: renaming changed line widths in two vitest specs. Reformatted
with the app's own @nextcloud/prettier-config; `prettier --check` over
CI's exact glob ("**/*.{js,ts,vue,css,scss}") is clean.
- eslint object-shorthand: `waardering: rating` became `rating: rating`,
which is the one ERROR among 164 warnings. Now `rating`.
node --check clean on every changed .js/.ts.
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ❌ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ❌ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 718/718 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-14 06:43 UTC
Download the full PDF report from the workflow artifacts.
The migration map legitimately contains `standaard => standard` — that is the register PROPERTY. The facet DIMENSION happens to share the spelling, and the frontend pass, which drives off that same map, renamed both. So the dimension I had deliberately restored two commits ago was renamed again by a later, more thorough pass. Both changes were individually right; together they were wrong. Restored in the four files that deal only in dimensions (services/facets.js, store/modules/facets.js and their specs). Everything else keeps the property rename. VERIFIED locally with the app's OWN runner — CI uses jest, not the vitest config in the repo, and vitest excludes src/** entirely, so `npx vitest` reported 'no test files found' and would have looked like a pass: npx jest -> 9 suites, 120 tests, all passing prettier --check over CI's exact glob -> clean
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 718/718 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-14 07:01 UTC
Download the full PDF report from the workflow artifacts.
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 718/718 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-14 07:25 UTC
Download the full PDF report from the workflow artifacts.
…ve touched
Playwright went from 12 failures to 5; these are the last two causes, and both
are the same class — a Dutch string that is DATA, not vocabulary.
gemmaType === 'standaardversie' a STORED value on GEMMA element objects.
Renamed, the compliance matrix filter
matched nothing and the view rendered empty.
COLUMN_SOURCE.STANDAARDVERSIE the enum compared against that value, so it
has to hold the same string.
DIMENSIONS ['... 'standard' ...] the e2e spec's copy of FacetController's
dimension list, which is Dutch on the wire.
That makes three separate places in this app where appears: a
register PROPERTY that moves, a facet DIMENSION that does not, and a gemmaType
VALUE that does not. Same spelling, three different contracts.
VERIFIED with the app's own runner: jest 9 suites / 120 tests green, prettier
clean over CI's glob.
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 718/718 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-14 07:51 UTC
Download the full PDF report from the workflow artifacts.
…lish, half Dutch The rename this PR ships emitted a new name whenever SOME token was translatable and let the rest through untouched, so it produced names like `accountantsverklaringRequired` and `afstandToArbeidsmarkt` — English grammar around a Dutch word. 22 of the names it introduced still carried one. Merging that is worse than not renaming: the schema ends up in a third language nobody can search for. The number is measured with real wordlists — 274,937 English words and 164,174 Dutch, a token counting as Dutch when the Dutch list has it and the English list does not. Two earlier instruments were wrong in opposite directions: matching against the translation dictionary found almost nothing, because the names that broke are built from words the dictionary never knew; matching against a hand-written English vocabulary flagged ordinary words like `transaction` and `income`. Control on the real one: `opbrengst`/`dienst`/`termijn` flag, `transaction`/`settlement`/`allocation` pass. CASE was wrong too. A dictionary value containing an underscore turned a camelCase name into snake_case mid-schema. Style now comes from the schema name that was REMOVED, never from the migration map's left-hand side — that side is a COLUMN name and is always snake_case, so asking it whether the original was snake_case answers yes for every multi-word name. My first attempt did exactly that and rewrote `adviesAuthority` as `advice_authority`. NO SECOND MIGRATION: the branch is unmerged, so these names have never existed in a database. The correction rewrites them to the final name everywhere, including the migration map's RIGHT-hand side, so the map points the original Dutch column straight at the correct English one. Every rename is registered in its snake spelling as well — the map is keyed on column names, and without that a camelCase correction never reaches it and the repair step would migrate data into a column the schema no longer declares. VERIFIED: PHPUnit identical to a control run of the branch without these corrections, phpstan [OK], psalm 0, eslint 0 errors, build OK, 0 surviving uses of any renamed property in src/, every register file parses. NOT IN SCOPE, measured rather than assumed: 62 property names that were ALREADY Dutch on development and which the first pass never touched. A separate tranche, not a defect in this PR.
The renames changed identifier lengths, so prettier's wrapping no longer matched in 31 files and `Frontend Check (format)` went red. `development` is fully prettier-clean, so this is drift the correction introduced, not pre-existing. Ran the project's own `format:fix`. `git diff -w` is attribute wrapping only and no import line moved — worth checking, because a formatter that reorders side-effect CSS imports changes behaviour while looking cosmetic. eslint 0 errors, build OK.
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ❌ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 719/719 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-14 12:33 UTC
Download the full PDF report from the workflow artifacts.
…split in two
Three vitest tests went red and development is fully green, so this was mine.
Both causes are the same shape: the rename updated one half of a pair.
1. SHORTHAND OBJECT PROPERTY. complianceMatrix.js reads `data.standardGemma`
(renamed, correct — that is the schema property) into a local variable that
kept its old name, and pushes it with shorthand:
const standaardGemma = … data.standardGemma …
unresolved.push({ moduleUuid, standaardGemma, evidenced, record })
A shorthand property has no `name:` for a key-rewrite to match, so the object
went on emitting `standaardGemma` while every consumer had moved to
`standardGemma`. The variable name IS the key here.
2. A COMPUTED AND ITS READERS. ComplianceMatrixView.vue declares
`standaardversies()` and reads `this.standard_versions` — the rename rewrote
the reads and not the declaration. That is not a test problem: the reads
resolve to undefined and `.length` throws when the view renders. No unit test
covers it; it was found only by chasing why one spec assertion failed.
`standaardversies` is a FUNCTION PARAMETER, not a schema property, so it is now
`standardVersions` in camelCase rather than the snake_case a column rename would
have produced — and every call site agrees, which they did not before: one spec
call passed `standard_versions` while three passed `standaardversies`.
VERIFIED: vitest 226/226, eslint 0 errors, build OK, format clean, PHPUnit
identical to control. `adminApi.spec.js` fails to collect with "window is not
defined" on this branch AND on development when run in isolation — pre-existing,
not touched here.
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 719/719 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-14 13:12 UTC
Download the full PDF report from the workflow artifacts.
…eaking the contract
`Frontend Tests (unit)` was red on CI and green locally, because this repo runs
BOTH runners: `test` is jest over `src/**.spec.js` and `test:unit` is vitest over
`tests/vitest`. My verification only ran vitest, so two failing jest specs were
invisible to me and obvious to CI.
The defect they caught: `FACET_DIMENSIONS` in src/services/facets.js is
`['referentiecomponent', 'standaard', 'applicatieservice', 'domein']`, and the
vocabulary pass translated exactly ONE of the four to `standard`. Those strings
are not ours to rename one at a time — `FacetController` and `FacetService` both
declare the same four as the query parameters and response keys of this app's own
facet endpoint. So the frontend began sending `standard[]` to a backend that only
reads `standaard[]`, and **facet filtering by standard silently returned
everything**: no 400, no console error, both sides behaving exactly as written.
Same shape as the DSO defect in openconnector — one member of a wire contract
translated, the rest untouched, which is hard to see in review precisely because
the surrounding lines still look right. Reverted, with a comment saying why the
four move together or not at all. Renaming all four plus the backend is a real
option, but it is an API change and belongs with the pre-existing tranche, not
smuggled into a vocabulary PR.
The two spec files are restored to development, since their sources are now
functionally identical to it.
Also in this commit, from the earlier round: a SHORTHAND object property
(`unresolved.push({ moduleUuid, standaardGemma, … })` — the variable name IS the
key, and a key-rewriter matching `name:` cannot see it) and a Vue computed whose
readers had been renamed without the declaration, which throws on `.length` at
render and no unit test covered.
VERIFIED: jest 120/120, vitest 226/226, eslint 0 errors, build OK, format clean.
`adminApi.spec.js` fails to collect with "window is not defined" here AND on
development in isolation — pre-existing.
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 719/719 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-14 13:34 UTC
Download the full PDF report from the workflow artifacts.
…names
Two more places where the rename moved one half of a pair, both caught by CI's
Playwright job and neither visible to jest, vitest, eslint or the build.
1. gemma-faceted-search.spec.ts keeps its OWN `DIMENSIONS` list, a duplicate of
the four the backend declares, and the pass had translated `standaard` there
too. With the source reverted to match FacetController/FacetService, the spec
was the only thing left asserting `standard`. Realigned, with the same comment
the source now carries: the four move as a set or not at all.
2. crud-persistence.spec.ts POSTs a module version with SHORTHAND —
`{ data: { versie, status } }` — so the local variable's name is the key. The
rename updated the READ two lines below (`r.version === versie`, property
access has a dot to match on) and could not see the write. The schema declares
`version` now, and MagicMapper DISCARDS an undeclared property with a log line
rather than a 4xx:
[MagicMapper] Discarding 1 property the schema "Application version"
does not declare: versie. They are NOT stored anywhere.
So the create "succeeded", the row came back without a version, and only the
later assertion noticed. Now written as `version: versie` explicitly, with a
note about why shorthand is wrong here.
That is the third shorthand-property defect in this programme — after procest's
BAG/WOZ query parameters and this app's own `unresolved.push({ moduleUuid,
standaardGemma, … })`. A key-rewriter matches `name:`; shorthand has no colon.
VERIFIED: jest 120/120, vitest 226/226, eslint 0 errors, format clean.
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 719/719 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ⏭️ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-14 13:55 UTC
Download the full PDF report from the workflow artifacts.
…ovider property (#526) gate-54 relation-dialect, check (d): three `x-relation-filter` tokens still name `@object.aanbieder`, a property that no longer exists. Commit aab66d5 ("translate Dutch vocabulary to English", #506, 2026-08-14) renamed the property key `aanbieder` -> `provider` everywhere, including the filter KEY side (`{ "aanbieder": ... }` -> `{ "provider": ... }`), but its value-side rule never matched the compound token `"@object.aanbieder"`. So these three findings are INTRODUCED, not legacy debt — they are two days old. Effect of the defect: ADR-062 rule 6 drops an unresolved token rather than filtering on nothing, so the three pickers (service.contactPerson, service.modules, module.contactPerson) have been rendering UNSCOPED since the rename — every contact person in the instance, not just the provider's. This is NOT a data migration. `x-relation-filter` is picker-scoping metadata naming a property of the same schema; no property and no stored value changes. RenameDutchCatalogColumns already migrated the stored data (lib/Repair/ RenameDutchCatalogColumns.php:179 `'aanbieder' => 'provider'`). Both sides of each token verified by hand (the gate only checks the value side): service.contactPerson {organization: @object.provider} -> contactPerson.organization OK, service.provider OK service.modules {provider: @object.provider} -> module.provider OK, service.provider OK module.contactPerson {organization: @object.provider} -> contactPerson.organization OK, module.provider OK gate-54, the gate's own vendored helper (check_relation_dialect.py, blob 3d4c81ca == ConductionNL/.github@main), over its own enumeration (git ls-files lib/Settings, register*.json + register.d/*.json): BEFORE: 3 findings over 3 files AFTER: 0 findings over 3 files
Applies the fleet Dutch→English pass: 93 property names plus identifiers, comments and docblock shapes. Extends
RenameDutchCatalogColumns(6 pairs → 84) rather than adding a second step.The pipeline aborted before touching anything, which is the point
relationalready has bothbronandsource, and their descriptions say the same thing — a pre-existing duplicate from a partial earlier rename. Renamingbronontosourcewould collide and lose data; inventing a third name would be worse.Consolidating them is a data decision for the owners, so
bronis excluded with that reason recorded.Two more exclusions, for fleet consistency
This app already shipped
beschrijving_kort → short_descriptionandbeschrijving_lang → description. My dictionary would have produceddescriptionKortanddescriptionLang— half Dutch, and forking the app against its own migration.It got as far as the registers (31 occurrences) before I noticed, because
kortandlangcarry none of the Dutch orthographic markers the residual guard looks for. Guard widened, both names excluded, branch rebuilt from clean.versieswas the same story (standaardVersies→standardVersies).Two defects I caused by hand, both bypassing the tool's guards
$moduleVersionSchemaIdwith a plain regex renamed the parameter but not its named arguments, breaking three call sites. The AST tool moves both together; my regex did not.namealias block inDataMappernormalised incomingnameonto the then-canonicalnaam. The canonical isnamenow, so it becameisset($data['name']) === false && isset($data['name']) === true— a contradiction that can never run. Removed rather than left as dead code.Verification
In the
nextcloudcontainer, against a control run of cleandevelopment:Not verified locally: the test suite. softwarecatalog's bootstrap loads every installed app, and
nldesignin this container is missing its vendor — the run dies before reaching a test, on the baseline too. CI must confirm the suite.