fix: mount CnDashboardPage, restore info.xml validity, document the throttler - #57
Conversation
…hrottler
Three pre-existing failures on `development`, plus the gate-69 finding, in the
app the fleet copies from — petstore is the reference implementation, so debt
here propagates by being imitated.
1. info.xml element ORDER (info.xml lint, exit 3)
app-info.xsd declares both `<info>` and `<dependencies>` as SEQUENCEs, and
this file violated both:
dependencies: php database command lib owncloud nextcloud architecture backend
-> <php> sat AFTER <nextcloud>
info: ... dependencies background-jobs repair-steps ... settings ... navigations
-> <navigations> sat BEFORE <repair-steps> and <settings>
Every element was valid; only the order was not, which is why the message
reads "Element 'php': This element is not expected. Expected is one of
( architecture, backend )". hrmq's info.xml carries a comment about the
identical defect. Order taken from the schema itself, not guessed, and the
result checked to be monotonic against that sequence.
2. Dashboard bypassed its own page component (gate-69 rule (a))
The `type: "dashboard"` page carried 8 widgets in a page-level `widgets[]`,
so CnPageRenderer rendered a bare CnWidgetGrid INSTEAD of CnDashboardPage
and the page never got its chrome. Moved to config.widgets + config.layout,
coordinates unchanged.
The nc-vue bump (2.3.0 -> 2.9.1) is REQUIRED for this, not cosmetic. Two of
the four widget types resolve only from BUILT_IN_WIDGETS, which
CnDashboardPage did not consult before nextcloud-vue#713:
chart-by-field -> layer1: petstore's own registry.js
stats-block -> layer2: dashboard registry
header -> layer3: BUILT_IN_WIDGETS
object-table -> layer3: BUILT_IN_WIDGETS
Converting on 2.3.0 would have left `header` and `object-table` rendering
nothing, on a page that looks correct in the manifest.
3. phpcs: PortalActionController::__construct documented four parameters for
five — `IThrottler $throttler` missing, which also pushed `$logger` out of
alignment. 2 errors, now 0.
Also carries the process/audit field lock from #56, which this supersedes.
Verified: structural check asserts all 8 widgets kept type, title, content and
grid coordinates and that no other page changed; check:manifest Ajv PASS;
gate-69 findings 0; build succeeds.
Not fixed here: `npm audit` reports 9 findings, all transitive and none
introduced by this branch — the cyclonedx high is already covered by #6, and
the dompurify chain comes through @toast-ui/editor inside nc-vue itself and
needs fixing upstream.
Correction: the nc-vue bump was NOT required for this conversionI claimed I derived it by grepping // CnStatWidget/index.js
registerDashboardWidget('stat', { renderer: __sfc_main, ... })
Verified in the browser rather than by grep: pipelinq runs nc-vue 2.3.0, declares What this changes: the bump here is a good idea on its own (2.3.0 → 2.9.1 is 6 minors of fixes, and it is what the fleet is standardising on) but it is not a precondition for the conversion, and the PR body should not have said it was. The conversion, the info.xml ordering fix and the phpcs fix are all unaffected. Same check applied to the sibling PRs: scholiq#541's bump is genuinely required — |
Quality Report — ConductionNL/petstore @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| test-unit | ✅ | ||||
| check-specs | ✅ | ||||
| check-manifest | ✅ | ||||
| composer | ❌ | ✅ 100/100 | |||
| npm | ✅ | ✅ 761/761 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ❌ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-21 07:57 UTC
Download the full PDF report from the workflow artifacts.
The third ordering defect in this file, and the reason it did not show up in
the previous commit: xmllint reports only the FIRST violation, so each fix
merely reveals the next one on the following CI run.
appinfo/info.xml:39: element post-migration: Schemas validity error :
Element 'post-migration': This element is not expected. Expected is ( uninstall ).
info.xsd declares repair-steps as the sequence
pre-migration post-migration live-migration install uninstall
and this file had `install` before `post-migration`. Both steps run the same
two repair classes, so nothing about behaviour changes — only the order the
schema demands.
Checked the whole tree in one pass this time rather than one CI round per
defect. Against the file as it stands on `development` that reports all three
at once:
<info>: navigations before repair-steps/settings
<dependencies>: nextcloud before php
<repair-steps>: install before post-migration
and against this file: all element sequences in order. The checker is shown to
FAIL on the unfixed input before being trusted.
…dit findings
`npm audit fix` could not run at all here: it aborted on a PRE-EXISTING peer
conflict that has nothing to do with any advisory —
Found: sass-loader@16.0.8
Could not resolve dependency:
peer sass-loader@"^17.0.0" from @nextcloud/webpack-vue-config@7.0.4
so every finding it could have fixed stayed unfixed, and the Security job kept
failing for a reason the log never named. hrmq, scholiq and pipelinq are all on
^17.0.0 already; petstore and decidesk were the stragglers.
With the peer conflict resolved, audit fix runs and the count drops 21 -> 17.
What remains needs BREAKING major bumps of dev tooling and is deliberately not
in this PR — @nextcloud/eslint-config@9, vitest@4, @cyclonedx/cyclonedx-npm@6.
Those are fleet-wide toolchain decisions, and #6 is already moving cyclonedx
independently. Two further notes for whoever picks that up:
- npm offers "@nextcloud/webpack-vue-config@4.0.0" as the fix for the
crypto-browserify chain, which is a DOWNGRADE from the 7.0.4 in use.
`fixAvailable` is not always a fix.
- the dompurify chain arrives through @toast-ui/editor inside
@conduction/nextcloud-vue itself, so it cannot be fixed from this repo.
Build verified after the sass-loader major: webpack compiles, check:manifest
Ajv PASS.
Quality Report — ConductionNL/petstore @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| test-unit | ✅ | ||||
| check-specs | ✅ | ||||
| check-manifest | ✅ | ||||
| composer | ❌ | ✅ 100/100 | |||
| npm | ✅ | ✅ 761/761 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-21 08:07 UTC
Download the full PDF report from the workflow artifacts.
Quality Report — ConductionNL/petstore @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| test-unit | ✅ | ||||
| check-specs | ✅ | ||||
| check-manifest | ✅ | ||||
| composer | ❌ | ✅ 100/100 | |||
| npm | ✅ | ✅ 761/761 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-21 08:21 UTC
Download the full PDF report from the workflow artifacts.
petstore and hrmq were the last two apps on eslint 8; everything else moved to
eslint 10 + @nextcloud/eslint-config 9 some time ago. This is not a version
bump — v9 requires eslint >= 10, and eslint 10 is incompatible with the whole
eslint-8-era plugin set this app carried.
Removed, because leaving them is worse than not upgrading at all:
@eslint/eslintrc, @eslint/js, @eslint/config-helpers,
@typescript-eslint/eslint-plugin@7, @vue/eslint-config-typescript@13,
eslint-plugin-vue@9, eslint-plugin-import, eslint-import-resolver-*
A stale eslint-plugin-vue@9 / vue-eslint-parser@9 hoists over the copies v9
bundles; `vue/base/setup-for-vue` then supplies NO parser, `typescript-eslint`
claims **/*.vue instead, and every SFC fails with "Parsing error: Expression
expected". eslint counts a parse failure as ONE finding and lints nothing else
in the file, so the entire Vue layer goes unchecked while the problem count
looks small. Verified this did NOT happen: the run reports real Vue rule
findings (vue/attribute-hyphenation, vue/new-line-between-multi-line-property),
which only a working parser can produce.
eslint.config.js -> eslint.config.mjs, copied from the fleet's canonical shape
(scholiq) with only the app-specific tail changed. It must be `.mjs` rather
than `"type": "module"`: v9 is ESM, but webpack.config.js, vitest.config.js and
the tests/** CLI checkers are CommonJS and would stop parsing.
engines: ^20 -> ^22.14 || ^24 || >=26, plus .nvmrc. v9 imports findPackageJSON
from node:module, which first exists in 22.14; on Node 20 eslint dies before
linting a single file and npm reports the mismatch only as an EBADENGINE
warning it continues past.
Also aligned, each one forced by the next:
vitest 1 -> 3 (@vitejs/plugin-vue@5 pins vite to ^5||^6, but
@vitejs/plugin-vue 5 -> 6 @nextcloud/vue's vue-router wants ^7||^8, so the
plugin had to move for the tree to resolve)
stylelint 15 -> 17, @nextcloud/stylelint-config 2 -> 3
sass-loader 16 -> 17 (`npm audit fix` could not run at all until this
peer conflict was resolved)
Lint findings fixed rather than suppressed: 82 -> 0 errors. 71 were mechanical
(--fix), plus an unused catch binding (`catch (e)` -> `catch`) and `no-console`
set off to match scholiq/procest/pipelinq/decidesk/shillinq, whose remaining
uses here are console.warn on a failed fetch.
npm audit: 21 findings (1 critical, 3 high) -> 10 (0 critical, 1 high).
NOT done: @cyclonedx/cyclonedx-npm stays at ^4.2.1. v6 treats a non-clean
`npm ls` as fatal, and this tree cannot be made clean from here —
stylelint-config-recommended-vue@1.6.1 peers postcss-html@^1 while its own
stylelint-config-html@2 requires ^2, an upstream contradiction the whole fleet
carries. Bumping cyclonedx would trade a passing SBOM job for a failing one.
Verified: lint 0 errors, vitest 15/15, check:manifest Ajv PASS, webpack builds.
Quality Report — ConductionNL/petstore @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| test-unit | ✅ | ||||
| check-specs | ✅ | ||||
| check-manifest | ✅ | ||||
| composer | ❌ | ✅ 100/100 | |||
| npm | ✅ | ✅ 747/747 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-21 09:30 UTC
Download the full PDF report from the workflow artifacts.
gate-16 (spec-coverage) flagged one changed method with no @SPEC — true about the diff, false about the change. `UserDisplay.resolve()` appears only because eslint 10 flagged its unused catch binding (`catch (e)` -> `catch`). Used `@spec exclude` with the reason rather than pointing at a requirement. Resolving a UID to a display name is presentation detail with no petstore requirement behind it; naming an anchor would assert a traceability that does not exist and would then pass silently forever. For the record, this branch is net BETTER than development on gates: development: gate-4, 5, 14, 19, 25, 26 FAIL (6) this branch: gate-4, 5, 14 FAIL (3, all pre-existing)
Quality Report — ConductionNL/petstore @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| test-unit | ✅ | ||||
| check-specs | ✅ | ||||
| check-manifest | ✅ | ||||
| composer | ❌ | ✅ 100/100 | |||
| npm | ✅ | ✅ 747/747 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-21 09:57 UTC
Download the full PDF report from the workflow artifacts.
Two halves of the same defect (the openconnector#1173 rule: an app's floor must be >= the maximum floor of every <app> it depends on). petstore hard-depends on openregister — src/manifest.json declares "dependencies": ["openregister"] — and openregister declares <nextcloud min-version="32"/>. petstore declared 28. appinfo/info.xml min-version 28 -> 32 Below 32 the dependency refuses to install and every pet, category and order — all OpenRegister objects — is unreachable. Declaring 28 advertised an App Store range this app cannot deliver. code-quality.yml nextcloud-test-refs stable31 -> stable34 The stable31 cells could never enable openregister and failed at "Install Nextcloud" with the workflow's own words: "This is an ENVIRONMENT failure, not an application failure — the run cannot measure this repository without it." They were burning two matrix legs to tell us that, on every run, before a line of this repo's code executed. stable31 is REMOVED, not dropped for coverage: an untested range is an advertised claim we cannot support, and the app no longer claims it. The matrix now matches the fleet (procest, pipelinq, scholiq all run 34/32/33). info.xml element order re-checked against app-info.xsd after the edit: all sequences in order.
Quality Report — ConductionNL/petstore @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| test-unit | ✅ | ||||
| check-specs | ✅ | ||||
| check-manifest | ✅ | ||||
| composer | ❌ | ✅ 100/100 | |||
| npm | ✅ | ✅ 747/747 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-21 10:10 UTC
Download the full PDF report from the workflow artifacts.
… app
The dashboard rendered NOTHING — an empty page under the Nextcloud chrome —
and every CI check stayed green, because the failure is a runtime abort:
Error: Two different versions of Dexie loaded in the same app: 4.4.5 and 4.4.4
Dexie refuses to initialise twice and throws during boot, so no Vue component
ever mounts. Found by opening the page, not by any check.
Cause, in two layers:
1. @conduction/nextcloud-vue VENDORS dexie inside its published dist, at
dist/esm/node_modules/dexie/dist/dexie.js. It is not a resolvable
node_modules entry, so `npm ls dexie` reports exactly ONE copy and the
duplication is invisible from the dependency tree.
2. petstore also declared `dexie: ^4.0.8` directly — and never imports it
anywhere in src/. The bump to nc-vue 2.9.x moved the app's own copy to
4.4.5 while the vendored one stayed 4.4.4, and the two instances collided.
Removing the unused direct dependency leaves nc-vue's vendored copy as the only
one. Verified in a browser: the dashboard now renders `cn-dashboard-page` with
its header, 42 widget wrappers, 28 KPI cards and 3 tables.
Also kept: `dexie` added to the shared-vendor splitChunks group. It did not fix
this on its own — splitChunks cannot dedupe a copy that is already inlined in a
dependency's dist — but it is correct regardless, because with `default: false`
and `defaultVendors: false` nothing else can share a singleton library, and the
group is already attached before the others so no new <script> tag is needed.
The vendoring is an upstream packaging problem: a stateful singleton that
aborts on double-load should be a peer dependency, not inlined. Reporting to
ConductionNL/nextcloud-vue separately.
Final state — two failures remain, both externally owned
What this branch fixes
Verified in a browser, not only in CI
(An earlier revision of this comment lost its table cells to shell backtick expansion; this is the intended text.) |
Quality Report — ConductionNL/petstore @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| test-unit | ✅ | ||||
| check-specs | ✅ | ||||
| check-manifest | ✅ | ||||
| composer | ❌ | ✅ 100/100 | |||
| npm | ✅ | ✅ 746/746 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-21 10:20 UTC
Download the full PDF report from the workflow artifacts.
Clears three pre-existing failures on
developmentplus the gate-69 finding. petstore is the app the fleet copies from, so debt here propagates by being imitated.Supersedes #56 (carries its field-lock and phpcs fix).
1. info.xml element order —
info.xml lint, exit 3app-info.xsddeclares both<info>and<dependencies>as sequences, and this file violated both:dependencies<php>after<nextcloud>info<navigations>before<repair-steps>/<settings>Every element was valid; only the order was not — which is why the error reads "Element 'php': This element is not expected. Expected is one of ( architecture, backend )". hrmq's info.xml carries a comment about the identical defect. Order taken from the schema itself and the result checked monotonic against it.
2. Dashboard bypassed its own page component — gate-69 rule (a)
8 widgets sat in a page-level
widgets[], soCnPageRendererrendered a bareCnWidgetGridinstead ofCnDashboardPage. Moved toconfig.widgets+config.layout, coordinates unchanged.The nc-vue bump (2.3.0 → 2.9.1) is required for this, not cosmetic. Two of the four widget types resolve only from
BUILT_IN_WIDGETS, whichCnDashboardPagedid not consult before ConductionNL/nextcloud-vue#713:chart-by-fieldregistry.jsstats-blockheaderBUILT_IN_WIDGETSobject-tableBUILT_IN_WIDGETSConverting on 2.3.0 would have left
headerandobject-tablerendering nothing, on a page that looks correct in the manifest.3. phpcs — 2 errors → 0
PortalActionController::__constructdocumented four parameters for five (IThrottler $throttlermissing, which also pushed$loggerout of alignment).Verification
check:manifestAjv PASS (0 errors)Not fixed here
npm auditreports 9 findings, all transitive, none introduced by this branch. The cyclonedx high is already covered by #6; the dompurify chain arrives through@toast-ui/editorinside nc-vue itself and needs an upstream fix.