Skip to content

Release: merge development into beta - #351

Merged
rubenvdlinde merged 45 commits into
betafrom
development
Aug 30, 2026
Merged

Release: merge development into beta#351
rubenvdlinde merged 45 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.

#349)

The PHPUnit cell that runs the coverage ratchet fails with "scripts/coverage-guard.php predates merge-base comparison (it does not report the 'against' capability)". That message names a plausible cause rather than the measured one, but the underlying fact is simple: this copy is the 17,288-byte version and the workflow is written against the 35,733-byte canonical one, which additionally reports `deletion-neutral`. Without that capability, deleting well-tested dead code still reads as a coverage drop.

Measured across the 21-app fleet: 8 apps already carry the canonical copy and their ratchets run and report normally; 11 carry a stale variant. This is vendored-copy drift, the same shape as the six drifted app-manifest schemas.

Copied verbatim from ConductionNL/.github at quality-config/coverage-guard.php rather than patched locally. Reimplementing a gate has cost us before: a hand-rolled version found 1 finding where the real gate found 13.
…353)

This app declares schemas and shipped no demo data, so it opened on an empty
list: the person evaluating it had to author objects by hand against a schema
they did not know yet. Fleet-wide, 562 of 598 schemas were in that state.

🔴 GENERATED, NOT WRITTEN. Every value is derived from the schema that will
validate it — `enum` picks from the enum, `pattern` is satisfied, `format`
drives the shape, `minimum`/`maxLength` are honoured, `required` is always
populated. Hand-written demo data is wrong in a way nobody sees until the demo
(a status outside its own enum, a required field omitted) and it fails at
import, in front of whoever asked for the demo.

Produced and validated by the single copy that gate-99 also runs:
`vendor/conduction/hydra-gates/scripts/lib/generate_mock_register.py`.
Regenerate with that file; `--keep` preserves curated objects and tops up only
what is short.

🔴 IT DOES NOT INSTALL ITSELF (ADR-111 rule 3). `x-openregister.type: mock` is
imported ON DEMAND — sample data appearing on a production instance because
somebody upgraded is a data-integrity incident, not a convenience. Install it
with:

    occ openregister:descriptors:list --app=<app> --import=<register>

The setup-wizard step that offers this during first-run install (ADR-111 rule 4)
follows once OpenRegister's shared installer lands — deliberately not twenty-one
copies of the same logic.

Verified: `--check` re-validates every object against its own schema with
jsonschema and reports clean.

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/launchpad @ 911a1af

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
format
check-schema-l10n
composer ✅ 104/104
npm ✅ 538/538
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-27 15:45 UTC

Download the full PDF report from the workflow artifacts.

…eet (#354)

`e2e-skip-blocking` exists in the shared quality workflow and is enabled
in ZERO of the 21 fleet apps. It has never been on anywhere.

Its own description says to flip it per app once that app is worked down,
so that it "can then only stay clean". launchpad is the app to start
with: its last E2E run on development was 137 passed, 0 skipped.

Why it matters, measured today:

  - The CI log prints skip COUNTS with no reasons. A runtime
    `test.skip(cond, reason)` is visible ONLY in the Playwright report,
    which is exactly what this check reads. So today a broken fixture and
    a legitimate skip are indistinguishable after the fact.

  - 178 skipped specs across five apps: buildiq 62, decidiq 46, dossiq
    37, shillinq 21, integriq 12.

  - decidiq's integration-registry.spec.ts skips 21 tests on
    `test.skip(!first, 'no meeting objects on this instance')`. The CI
    seeder deliberately provisions registers and schemas only, so that
    condition means "no earlier spec happened to create a meeting" --
    a seeding-order problem, reported as 21 passes.

This is opened as a PR rather than pushed, so the gate runs against
launchpad here and reports its verdict before it can redden development.

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/launchpad @ 6338400

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-27 17:03 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/launchpad @ a71900d

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
format
check-schema-l10n
composer ✅ 104/104
npm ✅ 538/538
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-27 20:32 UTC

Download the full PDF report from the workflow artifacts.

)

launchpad is the only app in the fleet with e2e-skip-blocking: true, and
until now that flag did nothing: the E2E job printed

  ::warning::check_e2e_skips.py not found (hydra-gates not vendored here
  yet) - skip discipline NOT measured on this run

and went green. The app looked like the first in the fleet enforcing skip
discipline while measuring nothing.

Two causes, both now fixed upstream:

  1. check_e2e_skips.py existed only on .github main and was in NONE of
     the last 12 release tags. Cut as v1.10.0.
  2. The workflow looked for it WITHOUT the inner hydra-gates/ path
     segment that composer actually installs it under. Fixed in
     ConductionNL/.github#595, merged.

#595 makes a missing gate a hard FAILURE for an opted-in app rather than
a warning, so without this bump launchpad's next run would go red -- the
correct behaviour, and this is the fix it is asking for.

Verified locally: composer update resolves v1.10.0 and the file lands at
exactly the path the workflow probes:

  vendor/conduction/hydra-gates/hydra-gates/scripts/lib/check_e2e_skips.py
  14546 bytes

launchpad's last E2E run was 137 passed, 0 skipped, so the gate is
expected to pass on real data -- which is the whole point of turning it
on here first.

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/launchpad @ f625f50

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
format
check-schema-l10n
composer ✅ 104/104
npm ✅ 538/538
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-27 22:29 UTC

Download the full PDF report from the workflow artifacts.

…user (#364)

* fix(l10n): the eight manifest strings that render English to a Dutch user

gate-99 manifest-l10n-coverage fails on development: eight strings the
manifest renders have no key in l10n/nl.json, so a Dutch user sees the
English source. Nothing else reports it -- check:l10n-js compares nl.json
to nl.js, and a string absent from BOTH is in sync.

Flow and Flows are kept untranslated, which is what opencatalogi, dossiq,
openregister and integriq all already do.

Both files are updated together: adding to nl.json alone would trade this
gate for check:l10n-js.

* fix(l10n): the eight manifest strings that render English to a Dutch user

gate-99 manifest-l10n-coverage fails on development: eight strings the
manifest renders have no key in l10n/nl.json, so a Dutch user sees the
English source. Nothing else reports it -- check:l10n-js compares nl.json
to nl.js, and a string absent from BOTH is in sync.

Flow and Flows are kept untranslated, which is what opencatalogi, dossiq,
openregister and integriq all already do.

Both files are updated together: adding to nl.json alone would trade this
gate for check:l10n-js.
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/launchpad @ 5f201e4

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
format
check-schema-l10n
composer ✅ 104/104
npm ✅ 538/538
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-28 07:38 UTC

Download the full PDF report from the workflow artifacts.

…#366)

#354 turned this on. #356 removed it 16 minutes later while rewriting
this file's triggers -- not a revert, just two edits landing together and
one line not surviving.

Worth restoring now for a better reason than the first time. When #354
merged, the gate was a NO-OP: it could not find check_e2e_skips.py and
warned instead of failing, so launchpad looked like it was enforcing skip
discipline while measuring nothing. Three fixes later it genuinely runs:

  - hydra-gates v1.10.0 cut, containing the script (#359 vendored it)
  - ConductionNL/.github#595 fixed the lookup path, which had omitted the
    inner hydra-gates/ segment composer installs under, and made a
    missing gate a hard failure instead of a warning

On development's current head the gate EXECUTED and reported:

  0/137 tests skipped (0.0%) across 30 spec files
    V1 spec files executing ZERO tests            : 0
    V2 skips deferring to a deploy state CI decides: 0
    V3 skips/fixmes with no reason recorded       : 0
  Every skip names a real absence and every spec file ran something.

That answers the open question from #354: tests/e2e/docs-screenshots.spec.ts
does NOT trip the zero-test rule.

So this is measured, not hoped for. launchpad passes on real data, and the
flag can only keep it that way -- which is exactly what the input's own
description asks for: flip it per app once that app is worked down.

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/launchpad @ 53577f1

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
format
check-schema-l10n
composer ✅ 104/104
npm ✅ 538/538
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-28 08:42 UTC

Download the full PDF report from the workflow artifacts.

rubenvdlinde and others added 2 commits August 28, 2026 10:51
.github#597 set cancel-in-progress on the shared quality.yml, but a
caller-level concurrency cancels the whole run before the called
workflows setting can apply -- so that fix reached only the apps that
declare no concurrency of their own.

Measured 2026-08-28 over push runs on development since #597 merged:

    caller silent          0 of 11 cancelled
    caller says true       7 of 13 cancelled  (54%)

This repo is in the second group. pull_request keeps cancelling, where
superseding really is correct.
* refactor(manifest): the flow pages are an index and a flow

`flows` and `flow-detail` are deprecated aliases. `flows` predates named index
sources: a flow lives in OpenRegister's native flow table rather than a
register/schema pair, so an object-backed index had nothing to bind to and the
list needed a page type of its own. `config.entitySource` closes that, so the
list is an ordinary `index` and only the EDITOR still needs its own type, now
named `flow`.

Behaviour is unchanged - both aliases resolve to the same components. The list
page's `_note` is updated in the same commit: it asserted the old rationale
(that an index "cannot address" a flow), which this change disproves.

`config.app` is untouched and is load-bearing: the editor stamps it on a flow
created there and the index filters on it.

Requires @conduction/nextcloud-vue 2.21, where a named source's columns and
create button are actually read.

* chore(deps): bump @conduction/nextcloud-vue to ^2.21.0

The flow pages need 2.21.0: earlier releases DECLARE a named index source's
columns, create button and row actions without reading them, so the migrated
page renders a columnless table with no working create action.

The lock is the part that matters. CI installs with `npm ci`, which honours
package-lock.json and ignores how permissive the caret is — bumping the range
alone would change nothing about what actually installs.

---------

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/launchpad @ 2e9f724

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-28 09:06 UTC

Download the full PDF report from the workflow artifacts.

github-actions Bot and others added 2 commits August 28, 2026 11:38
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…e shell shows (#361)

* fix(dashboards): ship a dashboard, and let the API resolve the one the shell shows

A fresh install of launchpad had no dashboard at all. `/apps/launchpad/`
rendered "No dashboards available, contact your administrator",
`GET /api/dashboards` answered `{"items": []}` with HTTP 200, and
`oc_launchpad_dashboards` was empty. Two separate defects, and the first
one hides the second.

1. NOTHING EVER CREATED A DASHBOARD. `allowUserDashboards` defaults to
   `false` on purpose (REQ-ASET-003: an admin opts in before users may
   create personal dashboards). That default closes the only branch of
   `tryCreateFromTemplate()` that WRITES, so on a brand-new instance no
   dashboard was created by the app or by the user, and every rung of
   `resolveActiveDashboard()` returned null.

   The default widget bundle (the Conduction, Sendent and Nextcloud
   tiles plus a Files widget) had shipped and had unit tests. Nothing
   ever called it, because nothing ever created the dashboard it seeds.

   `SeedDefaultDashboard` now provisions it on install and after every
   upgrade, idempotently.

   THE SHAPE IS LOAD-BEARING. This app has two resolution chains and
   they do not agree. `getEffectiveDashboard()` consults admin
   templates; the page shell calls `resolveActiveDashboard()`, which
   reads only `getVisibleToUser()` and never looks at a template. A
   default ADMIN TEMPLATE therefore seeds four placements the workspace
   still will not render. Measured, not reasoned: the first cut of this
   fix seeded exactly that, and the app kept showing its empty state.
   The shape the shell resolves is a `group_shared` row on the reserved
   `default` group (REQ-DASH-012) with `isDefault = 1`, created
   `published` because `filterByPublicationState()` hides drafts from
   every non-owner and this row has none.

2. THE TWO CHAINS DISAGREED, SILENTLY. With the row seeded, the sidebar
   listed "DEFAULT / Dashboard" and the grid drew its empty state on top
   of it: `GET /api/dashboard` answered `{"error": "No dashboard
   available"}` for the same user the shell had just resolved.
   `getEffectiveDashboard()` had no group-shared rung at all. With no
   preference set it went straight from owned dashboards to shares and
   templates. It now resolves the `default` sentinel the same way
   `resolveActiveDashboard()` steps 3 and 5 do.

   Only the sentinel. A per-group default needs the caller's primary
   group, which this signature does not carry, and guessing one would
   make the two chains disagree in a new way.

Verified on a running instance: `occ maintenance:repair` seeds the row
(`group_shared` / `default` / `isDefault=1` / `published`) and
`/apps/launchpad/` renders Conduction, Sendent, Nextcloud and Files.
1577 unit tests pass; phpcs, psalm and phpstan clean on the changed files.

* test(repair): cover SeedDefaultDashboard, which shipped with none

`Guard coverage baseline` was red on the scoped ratchet, not on the suite —
PHPUnit itself passed (1577 tests, 4059 assertions):

    Surviving code, head:   75.05%  (716/954 statements)
    Surviving code, base:   76.83%  (693/902 statements)
    FAIL: coverage of the code this change KEEPS or ADDS dropped by 1.78%.

The PR added 52 statements and covered 23 of them. `lib/Repair/SeedDefaultDashboard.php`
(125 lines) arrived with no test at all; DashboardService got one, the repair
step did not.

Five tests over all four paths: the step's name, a seed that provisions, a seed
that finds one already there, a seed that throws, and the container failing to
resolve the service at all.

The two failure tests are the ones worth having. A repair step that lets an
exception escape aborts `occ upgrade` for the WHOLE instance — a missing demo
dashboard is cosmetic, a failed upgrade is not — so the step swallows and warns,
and without a test that asserts the swallow a later refactor could turn the one
into the other silently.

Both info branches are asserted on their MESSAGE, not just their call count:
they both call $output->info(), so a count-only test would pass with the two
swapped.

Local: OK (5 tests, 17 assertions).

* refactor(dashboards): extract the default-group rung, which took the method past phpmd

`PHP Quality (phpmd)` was red on one violation:

    lib/Service/DashboardService.php:435  ExcessiveMethodLength
    The method getEffectiveDashboard() has 104 lines of code.
    Current threshold is set to 100.

The group-shared rung this PR added is what pushed it over, so that is what
comes out — into resolveDefaultGroupDashboard(), carrying its own explanation
of why the rung exists at all (the page shell and the API resolved differently,
silently, so the sidebar listed a dashboard the grid drew an empty state over).

Behaviour is unchanged: same resolveGroupSharedDashboard() call with the same
DEFAULT_GROUP_ID sentinel, in the same position in the chain, returning the same
built result. The early-return is inverted only to keep the extracted method
flat.

Verified locally: php -l clean, phpmd exit 0 on BOTH legs (the ruleset and the
unused-params ruleset, as CI runs them), PHPUnit OK (1582 tests, 4076
assertions).

---------

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Co-authored-by: Ruben van der Linde <juan.claude@conduction.nl>
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/launchpad @ 18c7ea5

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
format
check-schema-l10n
composer ✅ 104/104
npm ✅ 538/538
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-28 10:14 UTC

Download the full PDF report from the workflow artifacts.

rubenvdlinde and others added 4 commits August 28, 2026 15:20
)

Three specs assert "No dashboards available". #361 (c9e5808) exists to
make that message impossible: SeedDefaultDashboard provisions an
INSTANCE-WIDE, group-shared dashboard on install and after every upgrade,
because a fresh install greeting a user with an empty shell was the defect.

So the specs are not wrong about the app; the app deliberately removed the
state they describe. They have been red on development since that commit.

Handled three different ways, on purpose:

1. active-dashboard-resolution.spec.ts — INVERTED, not deleted.
   The valuable assertion is the opposite of what it said: a brand-new
   account, which owns no dashboard of its own, must still resolve to the
   seeded one. That is exactly the guarantee #361 introduced, and nothing
   was testing it.

2. runtime-shell-canEdit.spec.ts x2 — excluded WITH a reason.
   These assert the presence/absence of a Create CTA *inside* the empty
   state, so inverting them is not possible: the container is gone. The
   rule underneath (REQ-ASET-003 — allowUserDashboards gates whether a
   user may create a dashboard) is still worth covering and now needs an
   assertion that does not route through the empty state. The reason says
   so, names the commit, and reaches the Playwright report — so the
   skip-discipline gate reads it as attributed rather than silent.

3. fixtures/secondary-user.ts — the docblock said a brand-new account has
   zero dashboards "personal, group, or default", and that this was how
   the empty-state scenarios reached their state. That premise is now
   false and would have sent the next person down the same path.

Verified: npm ci rc=0, prettier clean, `playwright test --list` compiles
11 tests across both files.

Refs #372.

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Adds `launchpad-compose.yaml` and a setup page describing it.

The compose brings up Postgres and Nextcloud, installs openregister (required),
thematiq and integriq (optional) and launchpad from release tarballs, and enables
them in dependency order. Nothing is bind-mounted: Nextcloud installs an app by
deleting its directory and extracting an archive over it, so pointing that at a
checkout deletes the working tree — measured on a development machine on
2026-08-27, where an app-store update fired on a container restart and removed
every top-level file including .git.

Release tarballs rather than a clone for a second reason: a tarball is a
complete app carrying vendor/ and the built js/, and an app with no vendor/
does not fail loudly — it warns once and keeps loading, so it looks installed
while every service needing a dependency is absent.

The openregister dependency is not declared in appinfo/info.xml — no app in the
fleet declares an <app> dependency — so the compose encodes what the manifest
does not.

Verified: docker compose config parses and interpolates; the same generated
file was booted end to end for portaliq, which produced 17 registers, 86
schemas and 13 magic tables for its own register, with the portal content API
returning a real site rather than an empty shell.

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Two defects, both measured against a booted demo rather than inferred from
the code.

The verification step told you to run

  curl -s -o /dev/null -w '%{http_code}' http://localhost:PORT/apps/APP/

and described a pass. That request is unauthenticated, and a Nextcloud app
page requires a login, so it prints 401 on a perfectly healthy demo. A reader
following the page would conclude the demo was broken. The command now sends
the demo credentials and says in words that a bare 401 is expected.

Thematiq additionally has no app route at all — it declares
<admin-section>theming</admin-section> — so /apps/thematiq/ answers 404 even
authenticated. Its page now opens Settings -> Administration -> Theming.

Verified on two booted demos: the corrected commands return 200.

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
… state (#380)

The last red spec on launchpad's development. #362 fixme'd two empty-state
tests when #361 (c9e5808) made that state unreachable; this third one
used the same locator and was missed, so it kept failing:

    Error: expect(locator).toBeVisible() failed
    Locator: locator('.workspace-shell__empty-cta')
    Error: element(s) not found

SeedDefaultDashboard now provisions an instance-wide, group-shared
dashboard on install and after every upgrade, so no account resolves to
zero dashboards and no user ever sees the empty-state CTA.

The CTA was never the claim. The claim is REQ-ASET-003 — an ordinary user
may create a dashboard when allowUserDashboards is on — and that is what
actually regressed once: `dashboard.create` 403'd and the user was
stranded on the empty state. The sibling fixmes say so themselves: "the
underlying rule is still worth covering, but it needs an assertion that
does not route through the empty state."

This is that assertion. It POSTs to the endpoint as a throwaway
non-admin and requires 200/201.

Three details carried over from the file's own hard-won conventions,
rather than reinvented:

  - Basic-Auth + OCS-APIRequest via the existing `userApi` helper. A
    cookie-authenticated POST with no `requesttoken` is rejected by
    Nextcloud's CSRF check before reaching the controller, which would
    fail this test for a reason unrelated to authorization.
  - `expect([200, 201]).toContain(status)`, NOT `not.toBe(403)`. The
    weaker form also passes on 401 and 404, neither of which proves an
    ordinary user may create anything. That exact false green is
    documented two tests above.
  - The route is `dashboardApi#create`, `POST /api/dashboard`, read from
    appinfo/routes.php. The same file records an earlier draft that used
    a made-up path, got 404, and would have passed a weaker check.

`create()` takes only optional parameters and is `#[NoAdminRequired]`, so
a bare POST reaches `denyCreate()` — which is precisely the gate under
test.

Verified: npm ci rc=0, npm run lint rc=0, prettier clean, and
`playwright test --list` compiles all 8 tests in the file.

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
rubenvdlinde and others added 23 commits August 29, 2026 13:56
…licks (#393)

@conduction/nextcloud-vue 2.22.x made the product walkthrough actually open.
A `placement: "center"` welcome step used to be parked in `_pendingAutoTour`
and never shown; the library now correctly starts it on any route. Its
`cn-walkthrough__dim--full` layer is a `role="dialog" aria-modal="true"`
overlay, so every spec that clicks behind it times out, and
`getByRole('dialog').first()` resolves to the dim layer rather than the
modal under test.

The marker is per USER, not per test, so leaving it unseeded also makes the
suite order-dependent: whichever spec runs first wears the tour.

Seeds the same marker dossiq's global-setup already seeds, with a sentinel
above any real app version so the tour composes to an empty step set.

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
The walkthrough-marker seed added to this file is not Prettier-clean, so
`Frontend Check (format)` fails on development — and the E2E tier with it.
`prettier --write` only, no behaviour change.

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Two independent faults, either of which alone stops the docs site updating.

TRIGGER. This listened on a branch called `documentation`. Nobody has pushed
to one since 2026-05-25, so every docs change merged to `development` passed
review and published nothing.

SECRETS. A reusable workflow receives no secrets by default. With none mapped,
the callee's publish step finds CF_API_TOKEN empty and skips itself on its own
guard, and the run finishes GREEN having changed nothing. Fixing only the
trigger would have produced exactly that.

The worker name is now pinned. Deriving it is the documented way to get a green
run that reaches nobody: wrangler creates the derived worker and publishes
there while the custom domains keep routing to the real one.

Where the app was renamed, `canonical-host` turns the retired hostname from a
second live copy of every page into a 301 to the same path on the current one.

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Bumps [stylelint](https://github.com/stylelint/stylelint) from 15.11.0 to 17.14.1.
- [Release notes](https://github.com/stylelint/stylelint/releases)
- [Changelog](https://github.com/stylelint/stylelint/blob/main/CHANGELOG.md)
- [Commits](stylelint/stylelint@15.11.0...17.14.1)

---
updated-dependencies:
- dependency-name: stylelint
  dependency-version: 17.14.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [@vue/test-utils](https://github.com/vuejs/test-utils) from 2.4.11 to 2.5.0.
- [Release notes](https://github.com/vuejs/test-utils/releases)
- [Commits](vuejs/test-utils@v2.4.11...v2.5.0)

---
updated-dependencies:
- dependency-name: "@vue/test-utils"
  dependency-version: 2.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.4.13 to 3.4.14.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](cure53/DOMPurify@3.4.13...3.4.14)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.4.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) from 3.2.7 to 4.1.11.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.11/packages/coverage-v8)

---
updated-dependencies:
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 4.1.11
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
)

Bumps [@cyclonedx/cyclonedx-npm](https://github.com/CycloneDX/cyclonedx-node-npm) from 5.0.0 to 6.0.1.
- [Release notes](https://github.com/CycloneDX/cyclonedx-node-npm/releases)
- [Changelog](https://github.com/CycloneDX/cyclonedx-node-npm/blob/main/HISTORY.md)
- [Commits](CycloneDX/cyclonedx-node-npm@v5.0.0...v6.0.1)

---
updated-dependencies:
- dependency-name: "@cyclonedx/cyclonedx-npm"
  dependency-version: 6.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [vue](https://github.com/vuejs/core) from 3.5.41 to 3.5.42.
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](vuejs/core@v3.5.41...v3.5.42)

---
updated-dependencies:
- dependency-name: vue
  dependency-version: 3.5.42
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [phpmetrics/phpmetrics](https://github.com/phpmetrics/PhpMetrics) from 2.9.1 to 2.11.0.
- [Release notes](https://github.com/phpmetrics/PhpMetrics/releases)
- [Changelog](https://github.com/phpmetrics/PhpMetrics/blob/master/CHANGELOG.md)
- [Commits](phpmetrics/PhpMetrics@v2.9.1...v2.11.0)

---
updated-dependencies:
- dependency-name: phpmetrics/phpmetrics
  dependency-version: 2.11.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…408)

Bumps [phpcsstandards/phpcsextra](https://github.com/PHPCSStandards/PHPCSExtra) from 1.5.0 to 1.5.1.
- [Release notes](https://github.com/PHPCSStandards/PHPCSExtra/releases)
- [Changelog](https://github.com/PHPCSStandards/PHPCSExtra/blob/develop/CHANGELOG.md)
- [Commits](PHPCSStandards/PHPCSExtra@1.5.0...1.5.1)

---
updated-dependencies:
- dependency-name: phpcsstandards/phpcsextra
  dependency-version: 1.5.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…407)

Bumps [squizlabs/php_codesniffer](https://github.com/PHPCSStandards/PHP_CodeSniffer) from 3.13.6 to 4.0.4.
- [Release notes](https://github.com/PHPCSStandards/PHP_CodeSniffer/releases)
- [Changelog](https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/4.x/CHANGELOG-3.x.md)
- [Commits](PHPCSStandards/PHP_CodeSniffer@3.13.6...4.0.4)

---
updated-dependencies:
- dependency-name: squizlabs/php_codesniffer
  dependency-version: 4.0.4
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [twig/twig](https://github.com/twigphp/Twig) from 3.27.0 to 3.28.0.
- [Release notes](https://github.com/twigphp/Twig/releases)
- [Changelog](https://github.com/twigphp/Twig/blob/3.x/CHANGELOG)
- [Commits](twigphp/Twig@v3.27.0...v3.28.0)

---
updated-dependencies:
- dependency-name: twig/twig
  dependency-version: 3.28.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan-phar-composer-source) from 2.2.8 to 2.2.9.
- [Commits](https://github.com/phpstan/phpstan-phar-composer-source/commits)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-version: 2.2.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [nextcloud/ocp](https://github.com/nextcloud-deps/ocp) from 34.0.2 to 34.0.3.
- [Commits](nextcloud-deps/ocp@v34.0.2...v34.0.3)

---
updated-dependencies:
- dependency-name: nextcloud/ocp
  dependency-version: 34.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…260830084126

chore(sync): carry beta back into development
beta held 19 commit(s) development did not. Merged with -s ours:
development's tree is kept BYTE FOR BYTE and only the ancestry is
recorded. That is the payload -- without it the merge base never moves
and the next development -> beta promotion conflicts on the version file
exactly as before. 13 of 19 promotion PRs were CONFLICTING for this
reason.

Nothing is silently imported. What beta holds and development does not,
and which this deliberately does NOT bring over:

  lib/Settings/launchpad_register.json

Those are dead Forgejo/Codeberg CI (removed from development on
2026-08-24/25 by 'chore(ci): remove dead Forgejo/Codeberg CI
configuration'), generated Docusaurus build output, and community-health
files that never existed on development. Each can be added deliberately
if wanted; resurrecting them as a side effect of a sync is how a merge
silently undoes a decision.
…0841

chore(sync): record beta's ancestry on development
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Two dependabot bumps landed on development on 2026-08-30 without CI, and each
leaves the tree unresolvable:

- @vitest/coverage-v8 went to 4.1.11 while vitest and @vitest/ui stayed on
  ^3.2.7, so coverage-v8 peers vitest 4.1.11 against @vitest/ui's vitest 3.2.7
- stylelint went to 17.14.1, which @nextcloud/stylelint-config 2.4.0 cannot
  peer: it wants ^15.6.0

Both are reverted to the version the rest of their own ecosystem is on, rather
than bumping the ecosystem, because a vitest 3 to 4 move is a separate change
that deserves its own testing.

Verified with npm ci --dry-run from the committed lock: exit 0.

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/launchpad @ d431b47

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-30 09:06 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/launchpad @ e1e9806

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
format
check-schema-l10n
composer ✅ 104/104
npm ✅ 544/544
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-30 10:17 UTC

Download the full PDF report from the workflow artifacts.

…426)

* fix(deps): hoist @vue/server-renderer so the unit suite can run

Every one of the 63 frontend test files failed, with no tests executed
at all:

  Error: Cannot find package '@vue/server-renderer' imported from
  node_modules/@vue/test-utils/dist/vue-test-utils.cjs.js

The package was installed, but NESTED at
node_modules/vue/node_modules/@vue/server-renderer rather than hoisted.
@vue/test-utils declares it as a PEER dependency ('3.x'), and a peer is
resolved upward from the importing package's own directory -- so
@vue/test-utils looked for node_modules/@vue/server-renderer, which did
not exist. Nothing was missing from the lockfile; it was in the wrong
place.

Declaring it directly pins it at the top level, which is where a peer
has to be. Version tracks vue itself (^3.5.42).

Verified locally: reproduced the failure first (1 file, 'no tests'),
then after the change the full suite runs -- 63 files passed, 682 tests
passed, 0 failed.

* fix(stylelint): extend the config the app actually declares

stylelint exited 78 -- a CONFIGURATION failure, not a lint finding:

  Could not find "stylelint-config-recommended".

stylelint.config.js extended 'stylelint-config-recommended-vue', which
was never declared in package.json. It was present only transitively,
and the config it in turn extends, stylelint-config-recommended, was not
installed at all.

launchpad already declares @nextcloud/stylelint-config ^2.4.0, and that
is what openregister, opencatalogi, dossiq and shillinq all extend. This
points the config at the package the app declares and the fleet uses,
rather than adding two more dependencies to prop up an outlier.

That made stylelint RUN, which surfaced 44 real violations the
configuration error had been hiding. 42 were auto-fixable
(rule-empty-line-before, plus a few over-indented selector continuation
lines) and were fixed with --fix; the diff outside css/ is whitespace
only.

The last two were a genuine CSS bug in css/header-override.css:

  background-color: #ffffff !important;
  background-image: none !important;
  background:       #ffffff !important;   <- discards both of the above

The shorthand alone already sets the colour and resets background-image
to none, so keeping only it preserves the computed result EXACTLY.
Keeping the longhands instead would not have, because the shorthand also
resets the other background sub-properties.

Verified: stylelint now exits 0.

* fix(stylelint): let Prettier own whitespace, and stop the fixer loop

The previous commit made stylelint run, and it and Prettier then
disagreed about the same six lines. Prettier indents a wrapped selector
list; stylelint's `indentation` rule demanded 0 tabs there. Running
either fixer broke the other check:

  npm run stylelint:fix  ->  Frontend Check (format) fails
  npm run format:fix     ->  Vue Quality (stylelint) fails

Both `indentation` and `string-quotes` are DEPRECATED in stylelint 15 --
it prints a deprecation warning for each on every run -- precisely
because formatters do this better. Turning them off resolves the conflict
in favour of the tool that owns formatting and leaves stylelint judging
what only it can judge: CSS semantics.

The three files are re-formatted to Prettier's shape.

Verified: stylelint exit 0 AND prettier exit 0 together, with the two
deprecation warnings gone.

---------

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/launchpad @ 63e7ee7

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
format
check-schema-l10n
composer ✅ 104/104
npm ✅ 544/544
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-30 11:06 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde
rubenvdlinde merged commit f2d58f1 into beta Aug 30, 2026
85 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant