Skip to content

Release: merge development into beta - #637

Open
github-actions[bot] wants to merge 21 commits into
betafrom
development
Open

Release: merge development into beta#637
github-actions[bot] wants to merge 21 commits into
betafrom
development

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Automated PR to sync development changes to beta for beta release.

Merging this PR will trigger the beta release workflow.

Reminder: Add a major, minor, or patch label to this PR to control the version bump. Default is patch.

github-actions Bot and others added 5 commits August 31, 2026 10:22
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.
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpinq @ deed7e1

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.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpinq @ 3c6ba36

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>
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpinq @ 0518633

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>
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpinq @ 6254d29

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>
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpinq @ eba3546

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.

rubenvdlinde and others added 2 commits September 1, 2026 06:56
* 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.
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpinq @ 82f0cfe

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.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpinq @ 08adc45

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>
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpinq @ c16da8e

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.
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpinq @ 9094410

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.

rubenvdlinde and others added 2 commits September 1, 2026 11:40
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.
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpinq @ c35ab5d

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.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpinq @ 0937146

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>
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpinq @ 5249b8f

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).
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpinq @ 8e8b042

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>
Comment thread tests/e2e/spec-coverage/settings-roadmap.spec.ts Fixed
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpinq @ 78cdf81

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>
Comment thread tests/e2e/spec-coverage/settings-roadmap.spec.ts Fixed
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpinq @ ece04e4

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>
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpinq @ b89fd3b

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.
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpinq @ 5b0bf90

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>
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpinq @ 3749c47

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants