Release: merge development into beta - #637
Conversation
The 0.2.7-beta.20260831102027 release bumped the version on beta. Without this, development stays behind beta and the next development -> beta promotion conflicts on the version file. Version files resolve to development's side, which is the higher line, so this never moves a version backwards.
) Per-widget `valueColor` is applied as an INLINE style on the number, so it beats the canonical card accent, and a hex literal opts the card out of NL Design System theming — which kpi-card.css forbids outright, because the nldesign app re-themes by overriding the Nextcloud tokens. Two outcomes, no third: * a colour that merely restated the default accent is deleted — `#0082c9` IS `--color-primary-element` in the default theme, so the tile looks identical and now re-themes correctly; * a colour that carried meaning becomes `variant`, which is themed and drives the icon tint and the number together so the two cannot disagree. `variant` on a stat/delta resolves through VARIANT_COLORS, which was moved onto the `-text` tokens first (nextcloud-vue#888) — the plain fill tokens failed WCAG AA at 1.08:1 as a foreground colour, and these conversions would have been the first tiles to hit that path. Edited as text rather than re-serialised, so the manifest keeps its hand-laid-out formatting; every `valueColor` in this file was confirmed to sit on a KPI widget first. Manifest schema validation passes.
Dependabot cannot propose composer updates for this app. Its updater fails
with:
Your requirements could not be resolved to an installable set of packages.
- edgedesign/phpqa[v1.27.0, ..., v1.27.2] require ext-xsl * ->
it is missing from your system.
config.platform pins php 8.3 so composer resolves against a known PHP version,
but says nothing about extensions. edgedesign/phpqa requires ext-xsl and the
resolving environment does not have it, so the resolve fails before any bump
can be computed.
CI is unaffected, which is why this went unnoticed: composer install replays
the committed lock and never re-resolves, so the pipeline stays green while
dependabot -- which does re-resolve -- fails every time. The shared quality.yml
installs no xsl extension anywhere and never invokes phpqa.
Declaring ext-xsl beside the php pin makes resolution assume exactly what the
committed lockfile already assumes. Verified in a clean composer:2 container
without --ignore-platform-reqs: the same file fails without this line and
resolves (103 installs, lock written) with it.
Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
…2.7-beta.20260831102027 chore(release): sync beta back into development
Levels this app with the fleet. 2.27.2 adds two fixes the earlier 2.27.0 pin does
not carry:
* headerless is no longer chromeless — a flat KPI card in a borderless wrapper
had no card, border or background at all;
* a stat `variant` paints from the `-text` tokens rather than the fill tokens,
which failed WCAG AA at 1.08:1 as a foreground colour. The KPI colour
cleanup converted hardcoded values to `variant`, so this is what makes those
conversions contrast-safe.
Lockfile only, and npm pruned nothing.
Quality Report — ConductionNL/larpinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ⏭️ | ||||
| phpcs | ⏭️ | ||||
| phpmd | ⏭️ | ||||
| psalm | ⏭️ | ||||
| phpstan | ⏭️ | ||||
| phpmetrics | ⏭️ | ||||
| eslint | ⏭️ | ||||
| stylelint | ⏭️ | ||||
| build | ⏭️ | ||||
| composer | ⏭️ | ⏭️ | |||
| npm | ⏭️ | ⏭️ | |||
| app:check-code | ⏭️ | ||||
| info.xml | ⏭️ | ||||
| REUSE | ⏭️ | ||||
| PHPUnit | ❌ | ||||
| Newman | ❌ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-31 11:44 UTC
Download the full PDF report from the workflow artifacts.
Quality Report — ConductionNL/larpinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 105/105 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-31 12:39 UTC
Download the full PDF report from the workflow artifacts.
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Quality Report — ConductionNL/larpinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 105/105 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-31 14:41 UTC
Download the full PDF report from the workflow artifacts.
Dependabot cannot update these packages. Its npm updater fails with: npm error code EOVERRIDE npm error Override for <pkg> conflicts with direct dependency The package is listed BOTH as a direct dependency and as a literal overrides entry pinned to the same range. Dependabot bumps the direct dependency and does not move the override, npm rejects the mismatched pair, and the update is abandoned -- so these packages can never be updated, security advisories included. npm's self-reference is the idiom for exactly this: $<name> tells the override to follow whatever the direct dependency resolves to, instead of a range that has to be kept in lockstep by hand. This repo already uses it (see the vue entry under @vue/test-utils). Transitive consumers still get pinned to one version -- the direct dependency's -- which is what the override was there to do. What changes is that the pin tracks the dependency instead of duplicating it. Verified in a clean container: bumping the direct dependency with the literal override reproduces the EOVERRIDE above; with the self-reference the same bump resolves. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Quality Report — ConductionNL/larpinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 105/105 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-31 17:15 UTC
Download the full PDF report from the workflow artifacts.
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Quality Report — ConductionNL/larpinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 105/105 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-31 20:45 UTC
Download the full PDF report from the workflow artifacts.
* chore(register): rename the skill slug to larping_skill Pipelinq also ships a schema slugged `skill`, an agent competence matched against request categories for routing. Larpinq's is a character skill bought with experience points. Slugs are global on a shared OpenRegister and a bare relation resolves by slug alone, so the two could bind to each other. Measured on the dev instance: schema id 55 (pipelinq) and id 1231 (larpinq). The prefix follows this register's own `larping_item` and `larping_event`, which exist for the same reason. The OBJECT TYPE stays `skill`. Only the slug it maps to changes, exactly as `item` maps to `larping_item`: - SettingsLoadService::OBJECT_TYPE_SCHEMA_SLUGS, the value only - PortalContributionProvider skillCatalog, a real schema binding - src/manifest.json, five real schema bindings - the register's own slug, three $refs and its schema list - three mock seed objects SettingsController::objectTypes and store.js SCHEMA_SLUGS are type keys, not slugs: both already list `item` and `event` rather than the prefixed slugs, so both stay as they are. * fix(portal): bind skillCatalog by dict key, matching its siblings PortalContributionProviderTest::testScopingMapMatchesRegisterAtHead caught this. The provider binds every collection by the register's DICT KEY, not by the slug: its 'event' and 'item' entries sit beside slugs larping_event and larping_item. Moving skillCatalog to the new slug broke that convention and the test that pins it. The slug rename itself stands. Only this binding goes back. * test(newman): point skillSchema at the renamed slug The API resolves an object collection by SLUG, which is why itemSchema is larping_item and eventSchema is larping_event. skillSchema was still 'skill' and the 'skill created (2xx)' assertion failed against a slug that no longer exists. --------- Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
The dashboard shipped two Refresh controls side by side. CnActionsMenu
renders one as the first item of the page-level Actions overflow menu, and
the manifest declared another as a header button next to that menu.
They did the same thing. A "type": "refresh" headerAction is dispatched as
emit(PAGE_REFRESH_CHANNEL, {}), which is the exact signal CnActionsMenu
broadcasts on refresh-channel="cn:page:refresh". Same channel, same
subscribers. Removing the header action takes away the duplicate and nothing
else.
An app that wants to keep a prominent Refresh button can stand the menu item
down with showRefresh: false instead. openregister's dashboard does that.
Shipping both is what this removes.
Quality Report — ConductionNL/larpinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 105/105 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ❌ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-09-01 05:03 UTC
Download the full PDF report from the workflow artifacts.
Quality Report — ConductionNL/larpinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 105/105 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-09-01 05:17 UTC
Download the full PDF report from the workflow artifacts.
…649) development E2E has been failing on: ::error::Larpinq schemas missing after import: [skill] ::error::The e2e suite cannot seed abilities, effects, skills or characters without them. The schema imported perfectly well. The seed was looking for the wrong name. ci-seed.sh matches on SLUG, and the run own diagnostic printed the answer: [ci-seed] schemas present: [... larping_attendance, larping_event, larping_item, larping_skill ...] larping_skill is right there. The required list already carried the larping_ prefix for item, event and attendance, so this is one entry that was missed when the others were prefixed. Fixed on the SEED side deliberately. An OpenRegister register slug is frozen once data references it, so renaming larping_skill to match the test would be the destructive way round → see the fleet renaming rules. Verified every required slug against the register files: all eleven are declared, none missing. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Quality Report — ConductionNL/larpinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 105/105 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-09-01 06:29 UTC
Download the full PDF report from the workflow artifacts.
…th URLs (#651) * fix(dashboard): drop the second Refresh from the dashboard header The dashboard shipped two Refresh controls side by side. CnActionsMenu renders one as the first item of the page-level Actions overflow menu, and the manifest declared another as a header button next to that menu. They did the same thing. A "type": "refresh" headerAction is dispatched as emit(PAGE_REFRESH_CHANNEL, {}), which is the exact signal CnActionsMenu broadcasts on refresh-channel="cn:page:refresh". Same channel, same subscribers. Removing the header action takes away the duplicate and nothing else. An app that wants to keep a prominent Refresh button can stand the menu item down with showRefresh: false instead. openregister's dashboard does that. Shipping both is what this removes. * feat(router): give larpinq the SPA catch-all, and move it to clean path URLs larpinq was the ONE app of the fleet's seven hash-routed apps that could not simply switch: its sub-paths 404'd at the server. Measured 2026-09-01, /apps/larpinq/characters and /events both returned 404 while every other hash-mode app answered 200 with the app shell.⚠️ Grepping routes.php for `catchAll`/`AppHost` had named the WRONG app. `Routes::standard()` installs the catch-all without either string appearing, so the grep measured whether the file MENTIONS it, not whether the route exists -- it flagged zaakafhandelapp (which was fine) and cleared larpinq (which was not). Only requesting a sub-path settles it. Three changes, in the order they had to happen: 1. DashboardController::catchAll(), delegating to page(), so the SPA is served on any sub-path. 2. appinfo/routes.php rebuilt on \OCA\OpenRegister\AppHost\Routes::standard(). Every existing route is passed through as $extra -- `standard()` lets an $extra route override a canonical one of the same name, so dashboard#page, settings#* and preferences#* keep their exact URLs and verbs, and domain routes stay ahead of the /{path} fallback. 3. createWebHashHistory -> createWebHistory, with routerBase() derived from the URL actually being served. Nextcloud serves the app under BOTH /apps/larpinq/... and /index.php/apps/larpinq/...; generateUrl() returns only one. This app's own specs use both spellings (/apps/larpinq in _nav.ts and docs-screenshots, /index.php/apps/larpinq in the visual spec), so either would break whichever base was fixed. Also de-hashed the DeepLinkRegistrationListener's 8 URL templates and their test. Those templates are handed to OTHER apps to link into larpinq, so a stale `#` would have kept sending every cross-app deep link to the dashboard -- silently, since the router resolves nothing and the catch-all redirects to `/`. Audited the four already-converted apps (stackiq, zaakafhandelapp, opencatalogi, keepiq): none register hash deep links, so nothing was shipped with that gap.⚠️ The catch-all does not take effect until Nextcloud's route cache is dropped. With memcache.local = APCu, `occ app:disable/enable` is NOT enough -- sub-paths kept 404ing with the correct routes.php already in place. Restarting the container cleared it and all four URLs went from 404 to routed. Verified against the published @conduction/nextcloud-vue (USE_LOCAL_LIB=false): /apps/larpinq/ -> Dashboard /apps/larpinq/characters -> Characters /index.php/apps/larpinq/characters -> Characters /index.php/apps/larpinq/events -> Events RELOAD on the /index.php form -> 200, still Events No hash anywhere. eslint exits 0, prettier --check on the FULL glob is clean, webpack compiles, and both PHP files pass php -l. The listener's unit test was updated in lockstep with the same 8 replacements; there is no phpunit in this checkout, so CI runs it.
Quality Report — ConductionNL/larpinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 105/105 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-09-01 08:59 UTC
Download the full PDF report from the workflow artifacts.
Follow-up to #651, which turned PHPUnit red on development: four errors, all reading Error: Class "OCA\OpenRegister\AppHost\Routes" not found Nextcloud includes appinfo/routes.php for EVERY larpinq request, and PHPUnit includes it without booting sibling apps at all, so an unguarded static call to a class owned by another app fatals. In production the same shape makes every route in the app 500 when openregister is absent -- not just the AppHost ones -- and larpinq does not declare <app>openregister</app>, so an admin can create exactly that configuration. class_exists() autoloads without fatalling when the class is unavailable. The fallback reproduces Routes::standard()'s output locally -- canonical routes first (minus any name the app's own overrides), then , then the SPA catch-all LAST so it never shadows a real route -- so larpinq still routes, catch-all included, without openregister. This is the pattern dossiq already uses (procest appinfo/routes.php, see decidesk#377 / #388); I should have copied it when adding the builder call rather than discovering it from a red pipeline. Verified: php -l clean, and requiring the file with NO openregister autoloadable returns 19 routes ending in dashboard#catchAll -- which is precisely the condition PHPUnit runs in. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
* fix(dashboard): drop the second Refresh from the dashboard header
The dashboard shipped two Refresh controls side by side. CnActionsMenu
renders one as the first item of the page-level Actions overflow menu, and
the manifest declared another as a header button next to that menu.
They did the same thing. A "type": "refresh" headerAction is dispatched as
emit(PAGE_REFRESH_CHANNEL, {}), which is the exact signal CnActionsMenu
broadcasts on refresh-channel="cn:page:refresh". Same channel, same
subscribers. Removing the header action takes away the duplicate and nothing
else.
An app that wants to keep a prominent Refresh button can stand the menu item
down with showRefresh: false instead. openregister's dashboard does that.
Shipping both is what this removes.
* chore(lint): let the linter see tests/ and scripts/
`npm run lint` was `eslint src`, so two whole trees were never linted. Across
the fleet that hid roughly 3,900 errors, none of which any CI leg had shown.
Most of it was the config, not the code, and the same two defects were in
every app.
**scripts/ had no config block at all.** These are CommonJS Node CLI checkers,
and flat config defaults every `.js` to ESM with browser-ish globals, so eslint
read the CommonJS wrapper itself as undefined identifiers: `require`,
`process`, `__dirname`, `__filename`, `module`. A `scripts/**` block now
declares the environment. Declaring beats suppressing here: `no-undef` is the
rule that catches a genuinely misspelled identifier, and dozens of fake
findings would bury a real one. A second block covers `scripts/**/*.mjs`, which
is ESM and needs Node's globals without the CommonJS wrapper.
**The tests block applied a non-TypeScript-aware rule to TypeScript.** It named
`tests/**/*.ts` while setting the CORE `no-unused-vars`, which v9 deliberately
turns off for `.ts` in favour of the `@typescript-eslint` version. The core
rule reads the parameter names inside a function TYPE as bindings, so
t?: (app: string, key: string) => string
reports `app` and `key` as unused variables, and every unused `catch (e)` in a
`.ts` spec reports twice. The block is split now: `.js`/`.mjs` on the core
rule, `.ts`/`.tsx` on the TypeScript one, same patterns on both.
Also: stale `eslint-disable` comments naming plugins eslint 10 no longer
registers, which are themselves errors ("Definition for rule ... was not
found"), and a rule that must not parse shell scripts.
The genuinely real findings were the useful part: dead locals, unused imports,
dead helper functions, unused `catch` bindings, extensionless relative imports,
and a handful of `== null` comparisons spelled out so they still match null AND
undefined.
Verified per app: `npm run lint` 0 errors over src + tests + scripts,
`prettier --check` clean, and the unit suite still green.
Quality Report — ConductionNL/larpinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 105/105 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-09-01 10:17 UTC
Download the full PDF report from the workflow artifacts.
Quality Report — ConductionNL/larpinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 105/105 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-09-01 11:23 UTC
Download the full PDF report from the workflow artifacts.
…and finish path routing (#657) larpinq development was red on PHPUnit (all six cells), E2E and two gates. Three separate causes. ## SettingsController::load() did not exist CanonicalSettingsRouteContractTest said it plainly: appinfo/routes.php routes to method(s) SettingsController does not define. Each is a dispatch-time 500: - OCA\Larpinq\Controller\SettingsController::load() `settings#load` is CANONICAL: OpenRegister\AppHost\Routes::standard() declares it, and the local fallback in appinfo/routes.php reproduces it for the case where openregister is absent. So the route existed with nothing behind it and every POST to /api/settings/load was a 500. larpinq was the outlier. dossiq, keepiq, decidiq and shillinq all define load(); checked all four. larpinq spells the same force-re-import operation `reimport()`, so load() delegates to it rather than duplicating, the way create() already delegates to update(). ## Specs still asserted hash URLs larpinq moved to path routing in #651, but three helpers still built `new RegExp("#/" + slug + …)`: Expected pattern: /#\/skills(\b|\/|$|\?)/ Received string: "http://localhost:8080/apps/larpinq/skills" The app was on the right page the whole time. Fixed in _nav.ts, index-pages.spec.ts and detail-forms-admin.spec.ts. ## A test that could not run, and asserted nothing if it had spa-ui.spec.ts "character form requires name field" threw TypeError: dialog.waitFor(...).catch(...).locator is not a function waitFor() resolves a PROMISE, so .locator() was chained onto a Promise. The locator it built was also DISCARDED, so even without the TypeError the test asserted nothing while carrying a name that says it checks the name field. It now awaits the dialog and asserts the field is visible. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Quality Report — ConductionNL/larpinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 105/105 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-09-01 13:36 UTC
Download the full PDF report from the workflow artifacts.
#651 moved larpinq to clean path URLs. The spa-ui helper kept navigating by `window.location.hash`, which was correct under hash routing and became a silent no-op the moment the router changed: the URL gains a fragment, `hashchange` fires, `createWebHistory` ignores it, and nothing throws. So 37 of this file's 52 cases never left the page they started on. They opened the SPA root and asserted against the dashboard while naming characters, events, players, skills, abilities, items, effects and conditions. The suite was green throughout, because the assertions hold on the dashboard too. `go()` now pushes through the router, with a pushState + popstate fallback that drives the listener history mode actually installs, and then asserts the URL is the route it asked for. That last check is what stops this from happening again: vue-router's catch-all rewrites an unresolved location to `/`, so without it a navigation that goes nowhere still passes. Verified against a live instance: 52 passed (10.8m).
Quality Report — ConductionNL/larpinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 105/105 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-09-01 16:35 UTC
Download the full PDF report from the workflow artifacts.
…mode (#663) Six settings-roadmap tests failed on the URL alone: Expected pattern: /#\/features-roadmap/ Received string: "http://localhost:8080/apps/larpinq/features-roadmap" The router moved to history mode (createWebHistory, src/main.js), so an in-app route is a real path with no '#'. The helper still demanded a hash, so it failed before ever reaching the .app-content assertion that is the real readiness gate. The app was right; the assertion was describing the old router. Its docblock still described 'mode: hash (fleet #133 deep-link fix)', which had stopped being true. Updated with it, because a stale comment is how the next person re-learns this the hard way. The new pattern is anchored at the end, so /features-roadmap cannot be satisfied by some longer route that merely contains it. Verified against a live instance rather than reasoned about: the file now reports 6 passed. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Quality Report — ConductionNL/larpinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ❌ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 105/105 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-09-01 17:39 UTC
Download the full PDF report from the workflow artifacts.
…d clear development (#666) * fix(routes): stop routing health and metrics larpinq cannot serve, and clear development larpinq's development has been red all day on three checks. Two of them are mine, from #651/#653.⚠️ gate-14 (route-reachability): "2 unrouted method(s) or wrong-target route(s)". The gate was RIGHT, and the finding is a latent fatal. #653's class_exists() fallback reproduces Routes::standard()'s output locally so larpinq still routes when OpenRegister is absent — and it copied `metrics#index` and `health#index` along with the rest. larpinq ships neither controller: lib/Controller holds Characters, Dashboard, Events, Preferences, Settings and Setup, and nothing else. Those two work on a normal instance because the branch ABOVE the fallback calls Routes::standard(), and OpenRegister's AppHost aliases the generic Metrics/Health controllers onto larpinq's conventional class names. Verified: /apps/larpinq/api/health answers 200 application/json and /api/metrics answers 200 Prometheus text. But the fallback runs ONLY when OpenRegister is absent, and then nothing aliases anything — so those two routes would have pointed at classes that do not exist, and a request to either would fatal rather than 404. Removed, with the reasoning written where the next person will copy the block. gate-25 (contract-coverage): `dashboard#catchAll` is a new public endpoint with no test. Covered in DashboardControllerTest, asserting equality with page() rather than merely "returns a response" — catchAll() exists only to delegate, and a delegation that quietly rendered the wrong template would give every deep link a blank page while still answering 200. Same test now guards zaakafhandelapp#619 and openregister#3303. Also aligned the fallback's catch-all with Routes::standard()'s `(?!api/)` lookahead. Without it a bare `.+` matches slashes and swallows unmatched `api/...` paths, answering the SPA shell at HTTP 200 — that is what ate all seventeen of zaakafhandelapp's ZGW resource routes (#619) and it would have behaved identically here whenever the fallback was in use. Third check, and NOT mine: prettier failed on tests/e2e/spec-coverage/settings-roadmap.spec.ts, last touched by #663. One statement that fits on a line after the hash was removed from it. Fixed here rather than left, since it keeps development red for everyone. php -l clean, prettier --check on the FULL glob clean, eslint 0 errors, DashboardControllerTest 5/5. The suite reports 5 errors locally, all `Class "Symfony\Component\HttpFoundation\HeaderUtils" not found` in the Characters/Events PDF download tests — a dev dependency missing from this checkout, present in CI, and untouched by this change. * fix(apphost): make the health/metrics generics explicit, so gate-14 can see them Follow-up on the same branch. gate-14 (route-reachability) went on failing with the identical "2 unrouted method(s) or wrong-target route(s)" after the routes were removed from the fallback, and the reason turned out to be two separate things.⚠️ FIRST, THE GATE WAS READING MY COMMENT. I removed the two routes and explained the removal in a comment that spelled their slugs out (`<controller>` + `#` + `<method>`). gate-14 matches that shape anywhere in appinfo/routes.php, comments included, so it kept reporting both — pointing at controller files that do not exist — long after the routes were gone. The comment is reworded and now warns about exactly this. SECOND, AND THE REAL FINDING. With the comment cleaned the gate still reported both, because it knows Routes::standard()'s canonical set and attributes those routes to any app that calls it. It exempts them only when the app shows, in its own code, that it adopts the generic behind them: either `\OCA\OpenRegister\AppHost\Bootstrap::register()`, or a registerService() of the generic controller in the same file that names it. larpinq did neither. Its /api/health and /api/metrics answer 200 on a normal instance purely because the AppHost stands in under larpinq's conventional class names — a real dependency that nothing in this repository stated. So it is stated now: Application::register() registers GenericHealthController and GenericMetricsController under OCA\Larpinq\Controller\{Health,Metrics}Controller, each behind a class_exists() guard so an instance without OpenRegister degrades instead of fataling — the same discipline as the autoloader prelude above it. Registered individually rather than adopting Bootstrap::register() wholesale, which would also alias dashboard, settings, preferences, repair steps and sections onto generics that do NOT match the controllers larpinq actually ships. Same reasoning and the same shape as shillinq's registerAppHostGenerics(). Gate run on this tree: gate-14 PASS, gate-25 PASS (was FAIL, FAIL). Two gates still fail LOCALLY — gate-22 and gate-53, both on `/pages/25/type` — because the vendored hydra-gates schema here predates the `flow` page type. CI reported neither, so its schema is newer; not touched. php -l clean, phpcs 0 errors, DashboardControllerTest 5/5. * refactor(apphost): extract registerAppHostGenerics from register() phpmd's ExcessiveMethodLength fires at 100 lines and register() landed on exactly 100 with the two registrations inline. Extracted to its own method, which is where procest and shillinq both put theirs — the gates push this decomposition, and the previous commit's comment already pointed at shillinq's registerAppHostGenerics() as the model. No behaviour change. phpmd clean, phpcs 0 errors, gate-14 and gate-25 still PASS, DashboardControllerTest 5/5. --------- Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Quality Report — ConductionNL/larpinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 105/105 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-09-01 21:06 UTC
Download the full PDF report from the workflow artifacts.
…ctory (#669) phpunit.xml collects ./tests/unit. A second ./tests/Unit existed beside it, holding Service/DemoDataServiceTest.php. On a case-sensitive filesystem those are different directories, so that file was never collected and its 7 tests have never run in CI. Nothing about the file was wrong, which is why this went unnoticed: it is green the moment it is collected. Collected tests go from 239 to 246. Found while working #667 (closed, superseded by #666); this is the one finding there that #666 does not cover. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Quality Report — ConductionNL/larpinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 105/105 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-09-01 21:45 UTC
Download the full PDF report from the workflow artifacts.
) * style(manifest): run prettier over src/manifest.json Formatting only: the JSON parses to an identical structure before and after. Its own commit because it is large and mechanical. The file was never prettier-clean, and Frontend Check (format) is scoped to changed FILES rather than changed lines, so any edit to the manifest fails that job until it is formatted once. * fix(observability): declare the health checks the endpoint reports #666 wired /api/health and /api/metrics to OpenRegister's generic controllers, which was the missing binding. This adds what they read. HealthCheckExecutor iterates the manifest's observability descriptors. With no observability block it iterates an empty list, so /api/health answers 200 with an empty `checks` object — it would report `ok` with the database down, which is the one thing a health endpoint must never do. Two checks, matching shillinq's: `database` (critical) and `openregister` (degraded). Metrics stay the implicit larpinq_info and larpinq_up gauges the engine adds. Also widens the URL escape in settings-roadmap.spec.ts. CodeQL reports the slash-only slash-escape as js/incomplete-sanitization, high: it leaves `.`, `?`, `+`, `(` and backslash live in the pattern. Nothing is exploitable — every route in that file is a literal — but a route containing a `.` would silently match more than it names. Verified against a live instance while the binding was in place: /api/health 200 returning exactly these two checks, both ok, and /api/metrics 401 anonymous.
Quality Report — ConductionNL/larpinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 105/105 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-09-01 22:32 UTC
Download the full PDF report from the workflow artifacts.
… inside (#674) its own card.⚠️ THE CAUSE IS AN INSET, NOT A COMPONENT. A card widget renders `flush` and then has its padding put BACK by the card-fit rule (`padding: 8px 14px`), so the KPI sits inside the wrapper while the WRAPPER draws the border, radius and background the user reads as "the card". The library's `--clickable:hover` rule then drew a 2px border and a drop shadow on the KPI itself, 8-14px in from the edge being hovered. Measured live on dossiq, while genuinely hovered: the tile carried a 2px rgb(0,103,158) border and its own shadow, inset 9px from the wrapper's top and 15px from its left. A non-card-fit tile sits at 1px, so its border lands on the wrapper's own edge and reads as one card. That inset is the whole difference, which is why it looked app-specific and was not. nextcloud-vue#932 moves the affordance to the wrapper. No layout change: nothing moves, one card outlines. Verified in the browser with a real pointer hover: before inner 2px rgb(0,103,158) + shadow wrapper grey, no shadow after inner transparent, no shadow wrapper rgb(0,103,158) + shadow Every app with clickable stat, gauge or delta tiles had this. This bump is one of the fleet sweep that clears it. Dependency change only: package.json and the nextcloud-vue entry in package-lock.json. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Quality Report — ConductionNL/larpinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 105/105 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-09-02 19:06 UTC
Download the full PDF report from the workflow artifacts.
Automated PR to sync development changes to beta for beta release.
Merging this PR will trigger the beta release workflow.