Skip to content

feat(quality): validate appinfo/info.xml against the App Store schema (Nextcloud's lint-info-xml) - #383

Merged
rubenvdlinde merged 1 commit into
mainfrom
feat/nextcloud-info-xml-and-check-code
Aug 12, 2026
Merged

feat(quality): validate appinfo/info.xml against the App Store schema (Nextcloud's lint-info-xml)#383
rubenvdlinde merged 1 commit into
mainfrom
feat/nextcloud-info-xml-and-check-code

Conversation

@rubenvdlinde

@rubenvdlinde rubenvdlinde commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Adopts the first of the five checks Nextcloud runs and we did not. quality.yml never referenced appinfo/info.xml at all, so a malformed manifest was discovered at App Store upload time — after the release was built, tagged and published.

It matters more now: the release path reads info.xml for the version the tag must match and the Nextcloud version whose occ signs the package. A file that doesn't validate can still parse well enough for a grep to return something plausible — which is how an unanchored pattern resolved 8 from <php min-version="8.3"/> in 13 apps earlier today.

Measured before merging — 5 pass, 13 fail

apps violation
doriath, opencatalogi <background-jobs> out of sequence
hermiq, portaliq, softwarecatalog <php> out of sequence
larpingapp, openbuild, scholiq <app> out of sequence
launchpad <types> out of sequence
nldesign <repair-steps> out of sequence
openconnector <category> out of sequence
openregister <documentation> out of sequence
zaakafhandelapp <summary> is 136 chars; the schema caps it

Twelve are element order — the schema is an xs:sequence. The thirteenth is a real content violation. All pre-existing, all would fail Nextcloud's own lint-info-xml, all a reorder away from passing.

Design notes

  • Schema fetched, not vendored. A vendored copy is a claim about what the App Store accepts that goes stale silently.
  • The fetch is asserted to have returned XML containing a schema element before any result is trusted. A validator that couldn't get its schema has validated nothing and must say so rather than pass.
  • Two extra assertions cover what validity alone does not: that <version> and <nextcloud min-version> exist. Their absence otherwise surfaces at release time.

Correction to the table above (2026-08-12)

"Twelve are element order" was wrong. Only seven were. The other six carried elements that do not exist in the App Store XSD at any position and could never be reordered into validity:

element apps why it was invisible at runtime
<dependencies><app> larpingapp, openbuild, scholiq ×2, openconnector DependencyAnalyzer::analyze() handles only architecture, php, database, command, lib, os and server version. It never reads app.
<groups> larpingapp OC_App sets $info['groups'] unconditionally from the enabled appconfig, overwriting whatever info.xml parsed.
<notification> openregister NC's InfoParser has no handling for it; the notifier is registered in PHP via registerNotifierService().
<types><search/> launchpad Not a valid app type; search is wired via registerSearchProvider().

The measurement was at fault, not the schema. xmllint stops reporting once an earlier error appears, so the "first error" column I built the table from masked everything behind it. A one-line-per-app summary of a validator that short-circuits is not an inventory of that app's problems — it is an inventory of each app's first problem.

Also hidden that way: <install> had to move after <post-migration> in six apps — the single most common violation in the fleet, absent from the original table entirely.

One real behaviour change came out of it. launchpad's <activity><provider> was an invalid direct child, and NC reads providers from activity.providers.provider — so OCA\LaunchPad\Activity\Extension had never been registered. Wrapping it in <providers> satisfies the schema and makes that feature work for the first time. Every other removal was verified inert against the Nextcloud server source before being touched.

All 13 now validate, verified by re-downloading each file from its remote branch and re-running xmllint. The fixes are commits on the existing chore/nextcloud-coding-standard PRs.

Nextcloud ships lint-info-xml.yml as a workflow template and every upstream app
runs it. This fleet ran no equivalent — quality.yml did not reference
appinfo/info.xml anywhere — so a malformed manifest was found at App Store
upload time, after the release had been built, tagged and published.

That matters more since the release path started reading info.xml for two things
it cannot get wrong: the version the tag must match, and the Nextcloud version
whose occ signs the package. A file that does not validate can still parse well
enough for a grep to return something plausible, which is exactly how an
unanchored min-version pattern resolved 8 from <php min-version="8.3"/> in 13
apps earlier today.

MEASURED BEFORE MERGING, against all 18 core apps: 5 pass, 13 fail.

    doriath, opencatalogi          <background-jobs> out of sequence
    hermiq, portaliq, softwarecatalog  <php> out of sequence
    larpingapp, openbuild, scholiq <app> out of sequence
    launchpad                      <types> out of sequence
    nldesign                       <repair-steps> out of sequence
    openconnector                  <category> out of sequence
    openregister                   <documentation> out of sequence
    zaakafhandelapp                <summary> is 136 chars; the schema caps it

Twelve of those are element ORDER — the schema is an xs:sequence and these files
list valid elements in the wrong positions. The thirteenth is a real content
violation. All are pre-existing, all would fail Nextcloud's own lint-info-xml,
and all are a reorder away from passing.

The schema is fetched from nextcloud/appstore rather than vendored: a vendored
copy is a claim about what the App Store accepts that goes stale silently. The
fetch is asserted to have returned XML containing a schema element before any
validation result is trusted — a validator that could not get its schema has
validated nothing, and must say so rather than pass.

Two extra assertions cover what validity alone does not: that <version> and
<nextcloud min-version> are actually present. Their absence surfaces at release
time otherwise, long after the PR that removed them merged.
rubenvdlinde added a commit to ConductionNL/opencatalogi that referenced this pull request Aug 12, 2026
The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. Nothing was added, removed or
reworded; <version> and the <nextcloud> min/max-version declaration are
unchanged.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.
rubenvdlinde added a commit to ConductionNL/portaliq that referenced this pull request Aug 12, 2026
The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. Nothing was added, removed or
reworded; <version> and the <nextcloud> min/max-version declaration are
unchanged.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.
rubenvdlinde added a commit to ConductionNL/softwarecatalog that referenced this pull request Aug 12, 2026
The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. Nothing was added, removed or
reworded; <version> and the <nextcloud> min/max-version declaration are
unchanged.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.
rubenvdlinde added a commit to ConductionNL/larpingapp that referenced this pull request Aug 12, 2026
The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. <version> and the <nextcloud>
min/max-version declaration are unchanged.

Top-level blocks are now repair-steps, settings, navigations; <php> and the
<database> entries precede <nextcloud>; and inside <repair-steps>,
<post-migration> precedes <install> (the schema's order is pre-migration,
post-migration, live-migration, install, uninstall - it does not reflect
execution order, which Nextcloud selects by event).

Ordering alone was not sufficient here. The <dependencies><app> entry is not
an ordering problem: the App Store schema has no <app> child under
<dependencies> at any position, so it can never validate. It was also inert -
OC\App\DependencyAnalyzer::analyze() handles only architecture, php, database,
command, lib, os and the server version, and never looks at "app" - so it
enforced nothing at install time. The dependency is now recorded as a comment
in the same place.
The <groups>larpers</groups> block was likewise not an ordering problem: the
schema has no <groups> element, and Nextcloud never read one - OC_App's app
listing sets $info['groups'] unconditionally from the app's 'enabled'
appconfig value, overwriting whatever info.xml parsed. So the block restricted
nothing. The requirement and the supported mechanism (app-level group
restriction via occ or the Apps admin page) are documented in the comment that
already stood above it.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.
rubenvdlinde added a commit to ConductionNL/openbuild that referenced this pull request Aug 12, 2026
The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. <version> and the <nextcloud>
min/max-version declaration are unchanged.

Top-level blocks are now background-jobs, repair-steps, commands, settings,
navigations; <php> precedes <nextcloud>; and inside <repair-steps>,
<post-migration> precedes <install> (the schema's order is pre-migration,
post-migration, live-migration, install, uninstall - it does not reflect
execution order, which Nextcloud selects by event).

Ordering alone was not sufficient here. The <dependencies><app> entry is not
an ordering problem: the App Store schema has no <app> child under
<dependencies> at any position, so it can never validate. It was also inert -
OC\App\DependencyAnalyzer::analyze() handles only architecture, php, database,
command, lib, os and the server version, and never looks at "app" - so it
enforced nothing at install time. The dependency is now recorded as a comment
in the same place.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.
rubenvdlinde added a commit to ConductionNL/scholiq that referenced this pull request Aug 12, 2026
The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. <version> and the <nextcloud>
min/max-version declaration are unchanged.

Top-level blocks are now background-jobs, repair-steps, settings, navigations;
<php> precedes <nextcloud>; and inside <repair-steps>, <post-migration>
precedes <install> (the schema's order is pre-migration, post-migration,
live-migration, install, uninstall - it does not reflect execution order,
which Nextcloud selects by event).

Ordering alone was not sufficient here. The <dependencies><app> entry is not
an ordering problem: the App Store schema has no <app> child under
<dependencies> at any position, so it can never validate. It was also inert -
OC\App\DependencyAnalyzer::analyze() handles only architecture, php, database,
command, lib, os and the server version, and never looks at "app" - so it
enforced nothing at install time. The dependency is now recorded as a comment
in the same place.
Both entries (openregister and openconnector) were converted; the openregister
version floor stays machine-readable as the constraint in
lib/Settings/scholiq_register.json.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.
rubenvdlinde added a commit to ConductionNL/launchpad that referenced this pull request Aug 12, 2026
The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. <version> and the <nextcloud>
min/max-version declaration are unchanged.

<commands> now precedes <settings>, and <navigations> follows <activity>;
inside <repair-steps>, <post-migration> precedes <install> (the schema's order
is pre-migration, post-migration, live-migration, install, uninstall - it does
not reflect execution order, which Nextcloud selects by event).

Two content fixes were unavoidable, as neither is an ordering problem.

<types><search/></types> is not a valid app type at any position: the schema
allows only prelogin, filesystem, authentication, extended_authentication,
logging, dav, prevent_group_restriction and session, and Nextcloud does
nothing with an unrecognised one. Unified search is already wired properly -
Application.php calls registerSearchProvider(LaunchPadSearchProvider) - so the
element was decorative. Replaced by a comment pointing at that registration.

<activity><provider> had to be wrapped in <providers>. The direct child is
what the schema rejects, and Nextcloud reads the provider list from
$info['activity']['providers']['provider'], so the Activity provider
(REQ-ACT-001) was in fact not being registered at all. The wrapping is
therefore a behaviour change: OCA\LaunchPad\Activity\Extension now actually
registers, which is what the requirement asked for.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.
rubenvdlinde added a commit to ConductionNL/nldesign that referenced this pull request Aug 12, 2026
The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. Nothing was added, removed or
reworded; <version> and the <nextcloud> min/max-version declaration are
unchanged.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.
rubenvdlinde added a commit to ConductionNL/openconnector that referenced this pull request Aug 12, 2026
The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. <version> and the <nextcloud>
min/max-version declaration are unchanged.

<category> was moved after <namespace> (the sequence is version, licence,
author, namespace, types, documentation, category), and the <dependencies>
children were put in php, database, lib, owncloud, nextcloud order.

Ordering alone was not sufficient here. The <dependencies><app> entry is not
an ordering problem: the App Store schema has no <app> child under
<dependencies> at any position, so it can never validate. It was also inert -
OC\App\DependencyAnalyzer::analyze() handles only architecture, php, database,
command, lib, os and the server version, and never looks at "app" - so it
enforced nothing at install time. The dependency is now recorded as a comment
in the same place.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.
rubenvdlinde added a commit to ConductionNL/openregister that referenced this pull request Aug 12, 2026
The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. <version> and the <nextcloud>
min/max-version declaration are unchanged.

<documentation> moved ahead of <category>; top-level blocks are now
background-jobs, repair-steps, commands, settings, activity, navigations,
contactsmenu; and <activity>'s children are in settings, filters, providers
order.

One content fix was unavoidable. <notification><service> is not an ordering
problem - the App Store schema has no <notification> element at any position,
and Nextcloud's InfoParser never read one, so the declaration registered
nothing. The notifier is registered in PHP, via
registerNotifierService(AnnotationNotifier::class) in lib/AppInfo/Application.php,
which is unaffected. Replaced by a comment pointing at that registration.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.
rubenvdlinde added a commit to ConductionNL/zaakafhandelapp that referenced this pull request Aug 12, 2026
…:sequence

The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant, and it caps <summary> at 128 characters. This
file was rejected by `xmllint --noout --schema info.xsd appinfo/info.xml` on
both counts. Nextcloud's lint-info-xml workflow validates against exactly that
schema, and ConductionNL/.github#383 adds the same check to the shared
pipeline.

<settings> now precedes <navigations>, per the sequence.

The English <summary> was 136 characters. Dropping the redundant "for
Nextcloud" brings it to 122 and keeps the meaning intact - the domain
(zaakgericht werken), the four capabilities and the OpenRegister foundation
all survive. The Dutch <summary> was already within the cap and is unchanged.
<version> and the <nextcloud> min/max-version declaration are unchanged.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.
rubenvdlinde added a commit to ConductionNL/doriath that referenced this pull request Aug 12, 2026
The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. Nothing was added, removed or
reworded; <version> and the <nextcloud> min/max-version declaration are
unchanged.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.
rubenvdlinde added a commit to ConductionNL/hermiq that referenced this pull request Aug 12, 2026
The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. Nothing was added, removed or
reworded; <version> and the <nextcloud> min/max-version declaration are
unchanged.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.
rubenvdlinde added a commit that referenced this pull request Aug 12, 2026
…check

Nextcloud ships phpunit-mariadb, phpunit-mysql, phpunit-oci and phpunit-sqlite
as separate workflow templates, because database-portability bugs are invisible
on a single backend. This fleet ran PostgreSQL only.

The `database` input already accepted sqlite, pgsql and mysql, but the phpunit
job wired exactly one of them: the postgres service was keyed off
`inputs.database`, and the install block had a pgsql branch with everything else
falling through to a bare `--database <x>` that works for sqlite and cannot work
for mysql — no host, no port, no credentials, and no service container to
connect to. So "mysql" was a value the input accepted and the job could not
honour.

WHAT CHANGED
- `database-test-matrix`, a JSON array, joins the phpunit matrix beside
  php-version and nextcloud-ref.
- A mysql:8.4 service sits beside postgres:16. Both are keyed off
  matrix.database rather than inputs.database, and a service whose image
  evaluates to an empty string is not started — which is how one job definition
  serves several backends.
- The install block is a case statement with real host/port/credentials per
  backend, and an explicit failure on an unknown one.
- pdo_mysql and mysqli join the extension list unconditionally. Conditional
  extensions would mean a mysql leg silently falling through and testing sqlite
  while its name said mysql.

INERT UNTIL AN APP OPTS IN. The default is an empty array, which collapses to
`[inputs.database]` — today's dimensions exactly. Both sides of that comparison
go through fromJSON on purpose: a string test against '[]' accepts '[ ]' and
'[""]' and then resolves to an EMPTY MATRIX VECTOR, and an empty vector does not
skip a job, it deletes it from the run with no row, no annotation and no trace.
Measured on this repo's own self-test for frontend-checks: 19 jobs instead of
21. Verified here against all three spellings of empty plus a real three-backend
array.

POSITIVE CONTROL IN THE JOB. `maintenance:install` can exit 0 having fallen back
to another backend, and a suite passing on sqlite while its leg is labelled
mysql is worse than no leg at all — it is a green cell asserting something about
a database it never touched. The step reads `occ config:system:get dbtype` back
and fails if it does not match the leg's own name.

That completes the five checks Nextcloud runs and we did not: info.xml XSD
(#383), app:check-code and REUSE (this branch), integrity:sign-app (#375), and
multi-database PHPUnit.
@rubenvdlinde
rubenvdlinde merged commit 5eea6bb into main Aug 12, 2026
28 of 31 checks passed
rubenvdlinde added a commit that referenced this pull request Aug 12, 2026
#383 (info.xml XSD) and this branch (app:check-code, REUSE, multi-database
PHPUnit) both insert jobs immediately before the Vue Quality group, so git saw
four conflicts that are all pure additions rather than competing edits. Resolved
by keeping both sides.

Verified after resolving: 20 jobs, no duplicate job names, YAML parses, and all
four of info-xml / app-check-code / reuse / phpunit are present. A keep-both
resolution that silently dropped one side would look identical to a clean merge
in the diff, so the job list is checked rather than assumed.
rubenvdlinde added a commit that referenced this pull request Aug 12, 2026
…, multi-DB PHPUnit (#385)

* feat(quality): adopt occ app:check-code and REUSE, the last two Nextcloud checks

Nextcloud runs five checks this fleet did not. #383 added the first
(info.xml against the App Store XSD). This adds two more; multi-database PHPUnit
is the remaining one and is a larger change to the phpunit job, tracked
separately.

occ app:check-code
------------------
Nextcloud's own tool for private and deprecated API usage. Our closest
equivalent was ONE hand-written PHPCS sniff covering ONE symbol —
\OC::$server, removed in NC 34 — written by hand precisely because static
analysis could not see the removal while nextcloud/ocp sat a major below the
declared min-version. app:check-code is the general form of that sniff: it knows
the whole private surface of the server it ships with, needs no per-symbol
maintenance, and moves forward when Nextcloud does.

The server is fetched at the version the app DECLARES, anchored to the
<nextcloud> element. Unanchored, the pattern returns 8 for thirteen fleet apps
(from <php min-version="8.3"/>) and a plausible-but-wrong 28 for a fourteenth.

REUSE
-----
Nextcloud runs reuse.yml on every app. We had a PHPCS sniff checking @license
and @copyright in PHP docblocks — a proxy for one file type that says nothing
about images, JSON, YAML, or the LICENSES/ directory REUSE requires.

It also closes a loop the coding-standard migration opened: the SPDX
InvalidEndChar exception in the shared ruleset exists because a trailing full
stop turns a machine-parsed SPDX expression into an invalid one. That exception
is only worth having if something actually parses those expressions.

BOTH ARE NON-BLOCKING BY DEFAULT, and that is a decision rather than timidity.
Every app here reaches into server internals somewhere — OCA\DAV, OC_App and
Doctrine\DBAL are all ignored by name in phpstan-base.neon — and only 2 of the
4 apps sampled ship a REUSE.toml at all. A gate that is red on arrival is a gate
nobody turns on: that is how openregister ended up with an E2E job that had
never once succeeded. Each carries a `*-blocking` input to flip per app once its
findings are worked down.

app:check-code carries a positive control. occ exits 0 on paths that inspect
nothing — an app directory it cannot see, a command that bailed on the
environment check — so the step asserts the log contains a recognisable verdict
before treating any exit code as evidence. A silent exit 0 must not read as
"no private API usage".

* feat(quality): multi-database PHPUnit — the fifth and last Nextcloud check

Nextcloud ships phpunit-mariadb, phpunit-mysql, phpunit-oci and phpunit-sqlite
as separate workflow templates, because database-portability bugs are invisible
on a single backend. This fleet ran PostgreSQL only.

The `database` input already accepted sqlite, pgsql and mysql, but the phpunit
job wired exactly one of them: the postgres service was keyed off
`inputs.database`, and the install block had a pgsql branch with everything else
falling through to a bare `--database <x>` that works for sqlite and cannot work
for mysql — no host, no port, no credentials, and no service container to
connect to. So "mysql" was a value the input accepted and the job could not
honour.

WHAT CHANGED
- `database-test-matrix`, a JSON array, joins the phpunit matrix beside
  php-version and nextcloud-ref.
- A mysql:8.4 service sits beside postgres:16. Both are keyed off
  matrix.database rather than inputs.database, and a service whose image
  evaluates to an empty string is not started — which is how one job definition
  serves several backends.
- The install block is a case statement with real host/port/credentials per
  backend, and an explicit failure on an unknown one.
- pdo_mysql and mysqli join the extension list unconditionally. Conditional
  extensions would mean a mysql leg silently falling through and testing sqlite
  while its name said mysql.

INERT UNTIL AN APP OPTS IN. The default is an empty array, which collapses to
`[inputs.database]` — today's dimensions exactly. Both sides of that comparison
go through fromJSON on purpose: a string test against '[]' accepts '[ ]' and
'[""]' and then resolves to an EMPTY MATRIX VECTOR, and an empty vector does not
skip a job, it deletes it from the run with no row, no annotation and no trace.
Measured on this repo's own self-test for frontend-checks: 19 jobs instead of
21. Verified here against all three spellings of empty plus a real three-backend
array.

POSITIVE CONTROL IN THE JOB. `maintenance:install` can exit 0 having fallen back
to another backend, and a suite passing on sqlite while its leg is labelled
mysql is worse than no leg at all — it is a green cell asserting something about
a database it never touched. The step reads `occ config:system:get dbtype` back
and fails if it does not match the leg's own name.

That completes the five checks Nextcloud runs and we did not: info.xml XSD
(#383), app:check-code and REUSE (this branch), integrity:sign-app (#375), and
multi-database PHPUnit.
rubenvdlinde added a commit to ConductionNL/larpingapp that referenced this pull request Aug 12, 2026
* chore: adopt nextcloud/coding-standard, .editorconfig and NC 34

Configuration only. The reformat is the next commit on purpose, so
.git-blame-ignore-revs can name a revision containing nothing but whitespace.

- .php-cs-fixer.dist.php + conduction/coding-standard, which extends
  nextcloud/coding-standard and can only ADD to it — enforced by that package's
  invariant test, not by review.
- cs:check / cs:fix now run php-cs-fixer. They were aliases for phpcs/phpcbf,
  so the documented Nextcloud command reformatted code AWAY from Nextcloud's
  standard.
- nextcloud/coding-standard dropped as a direct dependency. It arrives
  transitively at a version conduction/coding-standard has tested against;
  declared directly it was a dead dependency with no config and no invocation.
- phpcs.xml is now a stub over the shared semantics-only ruleset, and the local
  phpcs-custom-sniffs/ copy is gone. The fleet was carrying six divergent
  versions of NamedParametersSniff.php — a custom RULE, not a setting.
- .editorconfig, verbatim from nextcloud/server. No fleet app had one, so an
  editor configured by someone's previous Nextcloud work defaulted to tabs,
  which the old ruleset then rejected.
- nextcloud/ocp -> ^34.0 and PHPUnit -> stable34. This app declared support for
  NC 34 while being analysed against 31, so a symbol REMOVED in 32/33/34 was
  invisible to the type checker. That is why the NC 34 removal of \OC::$server
  needed a hand-written PHPCS sniff.
- the stylelint glob is quoted, so stylelint expands it rather than the shell.
  Unquoted, src/**/ matches exactly one directory level and nested components
  are silently unlinted.

gate-65 (coding-standard-adoption) enforces all of the above from
ConductionNL/.github@main. This app failed it; with this commit it passes.

* style: reformat with nextcloud/coding-standard — whitespace only

Applied by php-cs-fixer with conduction/coding-standard. Tabs, same-line braces,
(int)$x, single-space concatenation, ordered imports — Nextcloud's dialect, which
this app now passes unchanged. 57 file(s), no behaviour change.

Isolated from the configuration change so .git-blame-ignore-revs can name a
revision that touches nothing but formatting. Reviewing it line by line is not a
useful activity; the previous commit is the review.

* chore: ignore the reformat commit in git blame

6a673f0 touches 57 files and changes no behaviour. Without this, every line it
reflowed attributes to it and the real author is one --skip away.

GitHub honours the file automatically; locally it needs
`git config blame.ignoreRevsFile .git-blame-ignore-revs` once.

* fix: regenerate composer.lock for the new constraints

The previous commit changed composer.json without touching the lock, so
`composer install` refused with exit 4 and EVERY PHP job failed:

    Required (in require-dev) package "conduction/coding-standard" is not
    present in the lock file.
    Required (in require-dev) package "conduction/hydra-gates" is not present
    in the lock file.
    Required (in require-dev) package "nextcloud/ocp" is in the lock file as
    "v31.0.9" but that does not satisfy your constraint "^34.0".

Nothing was wrong with the reformat or the ruleset — the jobs never got as far
as running a tool. Measured on larpingapp#313 before this fix: phpcs, psalm,
phpstan and both PHPUnit legs red, all of them at `composer install`. Hydra
Gates passed in the same run, because it does not install composer
dependencies.

Now locked at conduction/coding-standard v1.0.0, conduction/hydra-gates v1.7.0,
nextcloud/ocp v34.0.2 — the last of which is the point of the exercise: this app
declares support for NC 34 and is now analysed against it.

* fix(appinfo): order info.xml elements per the App Store xs:sequence

The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. <version> and the <nextcloud>
min/max-version declaration are unchanged.

Top-level blocks are now repair-steps, settings, navigations; <php> and the
<database> entries precede <nextcloud>; and inside <repair-steps>,
<post-migration> precedes <install> (the schema's order is pre-migration,
post-migration, live-migration, install, uninstall - it does not reflect
execution order, which Nextcloud selects by event).

Ordering alone was not sufficient here. The <dependencies><app> entry is not
an ordering problem: the App Store schema has no <app> child under
<dependencies> at any position, so it can never validate. It was also inert -
OC\App\DependencyAnalyzer::analyze() handles only architecture, php, database,
command, lib, os and the server version, and never looks at "app" - so it
enforced nothing at install time. The dependency is now recorded as a comment
in the same place.
The <groups>larpers</groups> block was likewise not an ordering problem: the
schema has no <groups> element, and Nextcloud never read one - OC_App's app
listing sets $info['groups'] unconditionally from the app's 'enabled'
appconfig value, overwriting whatever info.xml parsed. So the block restricted
nothing. The requirement and the supported mechanism (app-level group
restriction via occ or the Apps admin page) are documented in the comment that
already stood above it.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.
rubenvdlinde pushed a commit that referenced this pull request Aug 12, 2026
#383's 5/13 was measured before it merged. Re-measured against every repo's
live development tip with the same App Store XSD: 6 pass, 12 fail. The
one-app difference is larpingapp, whose manifest was reordered by efa6aead at
11:28:48Z; validating that single file at both refs gives FAIL then PASS, so
the difference is the world moving rather than the instrument drifting.

Also records that the local validator reproduces the live CI message on
openregister byte for byte, and that the count decays.
rubenvdlinde added a commit to ConductionNL/doriath that referenced this pull request Aug 12, 2026
* chore: adopt nextcloud/coding-standard, .editorconfig and NC 34

Configuration only. The reformat is the next commit on purpose, so
.git-blame-ignore-revs can name a revision containing nothing but whitespace.

- .php-cs-fixer.dist.php + conduction/coding-standard, which extends
  nextcloud/coding-standard and can only ADD to it — enforced by that package's
  invariant test, not by review.
- cs:check / cs:fix now run php-cs-fixer. They were aliases for phpcs/phpcbf,
  so the documented Nextcloud command reformatted code AWAY from Nextcloud's
  standard.
- nextcloud/coding-standard dropped as a direct dependency. It arrives
  transitively at a version conduction/coding-standard has tested against;
  declared directly it was a dead dependency with no config and no invocation.
- phpcs.xml is now a stub over the shared semantics-only ruleset, and the local
  phpcs-custom-sniffs/ copy is gone. The fleet was carrying six divergent
  versions of NamedParametersSniff.php — a custom RULE, not a setting.
- .editorconfig, verbatim from nextcloud/server. No fleet app had one, so an
  editor configured by someone's previous Nextcloud work defaulted to tabs,
  which the old ruleset then rejected.
- nextcloud/ocp -> ^34.0 and PHPUnit -> stable34. This app declared support for
  NC 34 while being analysed against 31, so a symbol REMOVED in 32/33/34 was
  invisible to the type checker. That is why the NC 34 removal of \OC::$server
  needed a hand-written PHPCS sniff.
- the stylelint glob is quoted, so stylelint expands it rather than the shell.
  Unquoted, src/**/ matches exactly one directory level and nested components
  are silently unlinted.

gate-65 (coding-standard-adoption) enforces all of the above from
ConductionNL/.github@main. This app failed it; with this commit it passes.

* style: reformat with nextcloud/coding-standard — whitespace only

Applied by php-cs-fixer with conduction/coding-standard. Tabs, same-line braces,
(int)$x, single-space concatenation, ordered imports — Nextcloud's dialect, which
this app now passes unchanged. 426 file(s), no behaviour change.

Isolated from the configuration change so .git-blame-ignore-revs can name a
revision that touches nothing but formatting. Reviewing it line by line is not a
useful activity; the previous commit is the review.

* chore: ignore the reformat commit in git blame

8f2ab30 touches 426 files and changes no behaviour. Without this, every line it
reflowed attributes to it and the real author is one --skip away.

GitHub honours the file automatically; locally it needs
`git config blame.ignoreRevsFile .git-blame-ignore-revs` once.

* fix: regenerate composer.lock for the new constraints

The previous commit changed composer.json without touching the lock, so
`composer install` refused with exit 4 and EVERY PHP job failed:

    Required (in require-dev) package "conduction/coding-standard" is not
    present in the lock file.
    Required (in require-dev) package "conduction/hydra-gates" is not present
    in the lock file.
    Required (in require-dev) package "nextcloud/ocp" is in the lock file as
    "v31.0.9" but that does not satisfy your constraint "^34.0".

Nothing was wrong with the reformat or the ruleset — the jobs never got as far
as running a tool. Measured on larpingapp#313 before this fix: phpcs, psalm,
phpstan and both PHPUnit legs red, all of them at `composer install`. Hydra
Gates passed in the same run, because it does not install composer
dependencies.

Now locked at conduction/coding-standard v1.0.0, conduction/hydra-gates v1.7.0,
nextcloud/ocp v34.0.2 — the last of which is the point of the exercise: this app
declares support for NC 34 and is now analysed against it.

* fix(ci): pin symfony/string back to the 8.3-compatible line and clear 2 over-length lines

Every PHP job on this branch was red, phpcs included, and none of them had
reached their tool. The phpcs job log stops at composer install:

  - symfony/string v8.1.2 requires php >=8.4.1 -> your php version
    (8.3; overridden via config.platform, actual: 8.3.33) does not satisfy
    that requirement.
  ##[error]Your lock file does not contain a compatible set of packages.

composer.json pins config.platform.php to 8.3, but the lock regenerated in
the previous commit was resolved without that platform in force, so it took
symfony/string v8.1.2 — the first release of that package to require PHP
8.4.1. `composer update symfony/string --with-all-dependencies` under the
declared platform downgrades it to v7.4.15; the lock diff is that one
package and nothing else.

With composer install succeeding, phpcs then reported 2 real errors, both
Generic.Files.LineLength.MaxExceeded (185 and 166 characters) on the
@return description of registerFanOutShares(). The description moves into
the docblock body, where php-cs-fixer's phpdoc_align has no opinion about
its column.

Measured locally on php 8.3.33 with the branch's own vendor tree:
`phpcs --standard=phpcs.xml` exits 0 (768 @SPEC warnings remain, which
ignore_warnings_on_exit deliberately does not fail on), and
`php-cs-fixer fix --dry-run` finds 0 of 425 files to fix.

* fix(appinfo): order info.xml elements per the App Store xs:sequence

The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. Nothing was added, removed or
reworded; <version> and the <nextcloud> min/max-version declaration are
unchanged.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.
rubenvdlinde added a commit that referenced this pull request Aug 12, 2026
… and disable the dead app:check-code (#389)

TWO DEFECTS, plus a third found on the way in.

1. quality.yml produced ZERO JOBS fleet-wide since 11:26:16Z. #385 merged from a
branch written before #383; its hunks anchored on what had become info-xml's
Checkout step, and the clean textual merge spliced two jobs into a third —
app-check-code lost its steps:, reuse swallowed info-xml's body, a stray
sparse-checkout landed inside a run: block, and the final upload-artifact ended
up with two name: keys. Every core app consumes this file at @main, so both apps
that pushed after 11:26 got a run with no jobs at all (openconnector 31592994170,
larpingapp 31592027242). An unresolvable reusable workflow never goes red on its
own; only this repo's resolve probe noticed. Note for future linting: PyYAML's
safe_load parses the broken file happily and reports twenty jobs, so a generic
YAML lint would have called it clean.

2. None of the three Nextcloud legs was in the Quality Report's needs:, which is
the only meaningful required check across the fleet. Now 16 -> 19. That is three
jobs, not five: multi-database PHPUnit is a matrix dimension of phpunit and was
already covered, and integrity:sign-app lives in release.yml, which has no
Quality Report to gate.

3. occ app:check-code WAS REMOVED FROM NEXTCLOUD AFTER v20, so that job cannot
pass anywhere here. Extracting registered App-namespace commands from
core/register_command.php, same expression, only the tag varying: v20 yields
CheckCode alongside the others, v34 yields the others without it — v20 is the
control proving the extraction can produce the token. Corroborated on a second
artefact: core/Command/App/CheckCode.php is 200 at v20/v21 and 404 at v25 through
v34, while ListApps.php is 200 at every one of those tags. Every app here
declares min-version 31+, and the job's positive control carries an unconditional
exit 1 that sits above the check-code-blocking branch — so merging it into needs:
would have hard-failed all 18 PHP apps on an impossible check. enable-check-code
now defaults false, with the job left wired and still in needs: (a skipped result
is skipped, not failure) so the wiring is right when someone rebuilds the check.
The positive control was correct throughout: it refused to certify a run that
inspected nothing.

BLAST RADIUS, re-measured rather than quoted. info-xml has no blocking flag and
gates on arrival. Against every repo's live development tip: 6 pass, 12 fail —
not #383's 5/13. The difference is larpingapp, reordered by efa6aead at
11:28:48Z; that one file validates FAIL at the old ref and PASS at the new one.
Eleven of the twelve are xs:sequence element order; the twelfth is an over-length
summary. All are a manifest edit, not a code change. That work is not filed
anywhere yet.

reuse and info-xml are both proven to work — reuse passes in this PR's own
resolve probe, and info-xml produced a real schema error on openregister run
31591669849 that a local validator reproduces byte for byte. Only app-check-code
was dead. From needs: all three looked identical.

Also corrected: the queue was not held by a red guard. Six of the seven open PRs
carry a Shared-workflow guard pass from BEFORE the break — stale greens that
would flip red on any re-run. Only #378 is measurably blocked, by both defects.

check-code-blocking and reuse-blocking are untouched. Neither was asked to move.
rubenvdlinde added a commit to ConductionNL/zaakafhandelapp that referenced this pull request Aug 12, 2026
* chore: adopt nextcloud/coding-standard, .editorconfig and NC 34

Configuration only. The reformat is the next commit on purpose, so
.git-blame-ignore-revs can name a revision containing nothing but whitespace.

- .php-cs-fixer.dist.php + conduction/coding-standard, which extends
  nextcloud/coding-standard and can only ADD to it — enforced by that package's
  invariant test, not by review.
- cs:check / cs:fix now run php-cs-fixer. They were aliases for phpcs/phpcbf,
  so the documented Nextcloud command reformatted code AWAY from Nextcloud's
  standard.
- nextcloud/coding-standard dropped as a direct dependency. It arrives
  transitively at a version conduction/coding-standard has tested against;
  declared directly it was a dead dependency with no config and no invocation.
- phpcs.xml is now a stub over the shared semantics-only ruleset, and the local
  phpcs-custom-sniffs/ copy is gone. The fleet was carrying six divergent
  versions of NamedParametersSniff.php — a custom RULE, not a setting.
- .editorconfig, verbatim from nextcloud/server. No fleet app had one, so an
  editor configured by someone's previous Nextcloud work defaulted to tabs,
  which the old ruleset then rejected.
- nextcloud/ocp -> ^34.0 and PHPUnit -> stable34. This app declared support for
  NC 34 while being analysed against 31, so a symbol REMOVED in 32/33/34 was
  invisible to the type checker. That is why the NC 34 removal of \OC::$server
  needed a hand-written PHPCS sniff.
- the stylelint glob is quoted, so stylelint expands it rather than the shell.
  Unquoted, src/**/ matches exactly one directory level and nested components
  are silently unlinted.

gate-65 (coding-standard-adoption) enforces all of the above from
ConductionNL/.github@main. This app failed it; with this commit it passes.

* style: reformat with nextcloud/coding-standard — whitespace only

Applied by php-cs-fixer with conduction/coding-standard. Tabs, same-line braces,
(int)$x, single-space concatenation, ordered imports — Nextcloud's dialect, which
this app now passes unchanged. 89 file(s), no behaviour change.

Isolated from the configuration change so .git-blame-ignore-revs can name a
revision that touches nothing but formatting. Reviewing it line by line is not a
useful activity; the previous commit is the review.

* chore: ignore the reformat commit in git blame

abd0dba touches 89 files and changes no behaviour. Without this, every line it
reflowed attributes to it and the real author is one --skip away.

GitHub honours the file automatically; locally it needs
`git config blame.ignoreRevsFile .git-blame-ignore-revs` once.

* fix: regenerate composer.lock for the new constraints

The previous commit changed composer.json without touching the lock, so
`composer install` refused with exit 4 and EVERY PHP job failed:

    Required (in require-dev) package "conduction/coding-standard" is not
    present in the lock file.
    Required (in require-dev) package "conduction/hydra-gates" is not present
    in the lock file.
    Required (in require-dev) package "nextcloud/ocp" is in the lock file as
    "v31.0.9" but that does not satisfy your constraint "^34.0".

Nothing was wrong with the reformat or the ruleset — the jobs never got as far
as running a tool. Measured on larpingapp#313 before this fix: phpcs, psalm,
phpstan and both PHPUnit legs red, all of them at `composer install`. Hydra
Gates passed in the same run, because it does not install composer
dependencies.

Now locked at conduction/coding-standard v1.0.0, conduction/hydra-gates v1.7.0,
nextcloud/ocp v34.0.2 — the last of which is the point of the exercise: this app
declares support for NC 34 and is now analysed against it.

* fix(appinfo): shorten summary and order info.xml per the App Store xs:sequence

The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant, and it caps <summary> at 128 characters. This
file was rejected by `xmllint --noout --schema info.xsd appinfo/info.xml` on
both counts. Nextcloud's lint-info-xml workflow validates against exactly that
schema, and ConductionNL/.github#383 adds the same check to the shared
pipeline.

<settings> now precedes <navigations>, per the sequence.

The English <summary> was 136 characters. Dropping the redundant "for
Nextcloud" brings it to 122 and keeps the meaning intact - the domain
(zaakgericht werken), the four capabilities and the OpenRegister foundation
all survive. The Dutch <summary> was already within the cap and is unchanged.
<version> and the <nextcloud> min/max-version declaration are unchanged.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.

* fix(quality): pay off the #201 docblock/named-args debt the ruleset stub stopped demoting

The phpcs job on this branch failed for two different reasons in
succession, and only the second one is about this app's code.

At 1ef084d the job never reached phpcs at all:

  - Required (in require-dev) package "conduction/coding-standard" is not
    present in the lock file.
  - Required (in require-dev) package "conduction/hydra-gates" is not
    present in the lock file.
  ##[error]Process completed with exit code 4.

6e94b38 fixed the lock, composer install succeeded, and phpcs then ran for
the first time on this branch — reporting 613 errors. Those are not new
code. They are debt this app has always carried, and the old phpcs.xml said
so out loud:

  <!-- LEGACY DEBT (tracked in #201): this app
       pre-dates the tightened Conduction PHPCS commenting / named-parameter
       rules. Until a dedicated docblock + named-args cleanup lands, the
       following sniffs are demoted from errors to advisory warnings ... -->

Nine sniffs were demoted there, plus NamedParametersSniff. The migration
replaces phpcs.xml with a 12-line stub over the shared ruleset, and a stub
cannot carry an app-local demotion — so the whole of #201 promoted from
warning to error in a single config commit. Nothing was measured wrong; the
severity floor moved.

Rather than re-demote them (a stub that argues with the fleet ruleset is the
thing this migration exists to remove, and gate-65 would reject it), this
commit does the cleanup #201 was waiting for. All 613, across 52 files:

  186  CustomSniffs.Functions.NamedParameters.RequireNamedParameters
  173  PEAR.Commenting.FunctionComment.MissingParamTag
   75  PEAR.Commenting.FunctionComment.MissingReturn
   72  PEAR.Commenting.FunctionComment.Missing
   27  Squiz.PHP.DisallowInlineIf.Found
   16  Squiz.Operators.ComparisonOperatorUsage.ImplicitTrue
   15  Generic.Files.LineLength.MaxExceeded
   14  Squiz.Commenting.InlineComment.NotCapital
   12  Squiz.Commenting.VariableComment.Missing
   10  PEAR.Commenting.FunctionComment.ParamNameNoMatch
    7  Generic.Commenting.DocComment.ParamNotFirst
    3  Squiz.Operators.ComparisonOperatorUsage.NotAllowed
    3  (WrongStyle, MissingParamComment, SpacingBeforeShort)

Two of these carry real risk and were not applied mechanically.

ImplicitTrue's message says "use === TRUE instead", and taking that
literally is a bug factory: `filter_var($id, FILTER_VALIDATE_URL)` returns
the URL STRING, so `=== true` would have made that branch dead and passed
every URL through unstripped. Each site got the predicate that is actually
correct for its type. Where `empty()` was guarding a config string it was
kept as `empty($x) === true` rather than narrowed to `$x === ''`, because
`empty('0')` is true and a literal "0" register would otherwise have flipped
from unconfigured to configured.

A wrong named argument is a fatal Error at runtime that neither `php -l`,
phpcs, psalm nor an untouched test can see. Every one of the 186 was written
from the callee's real signature — `assertUuid(value: ...)` not `zaakId:`,
`getObjects(sort: $params['order'])` not `order:` — and the whole tree was
then re-checked by a tokenizer pass that resolves each `$this->`/`self::`/
`new` call to its declaration and compares labels: 276 named arguments, 0
mismatches. That check was shown to fail on an injected typo first.

Measured on php 8.3.33 with this branch's own vendor tree:
  phpcs        613 errors -> 0 (270 warnings remain: @SPEC and the SPDX
               InvalidEndChar exemption, both non-blocking by design)
  php-cs-fixer 0 of 88 files to fix — the two tools are not arguing
  php -l       clean over lib/
  PHPUnit      OK (268 tests, 825 assertions), unchanged from the baseline
  psalm        0 errors, same as the pre-change tree
  phpstan      0 errors, same as the pre-change tree

Auth posture is unchanged and was counted, not assumed: NoAdminRequired
132 -> 132, NoCSRFRequired 138 -> 138, @no-admin-idor-exempt 3 -> 3. Where
Generic.Commenting.DocComment.ParamNotFirst and the annotations' original
position were mutually exclusive, @PARAM moved above the annotations within
the same docblock; NC's ControllerMethodReflector scans the whole block, so
position is not load-bearing.

No sniff was suppressed and no @phpcs:ignore was added. Closes #201.

* fix(quality): remove the 16 else branches the ternary rewrites introduced

The previous commit rewrote 27 inline ternaries as if/else to satisfy
Squiz.PHP.DisallowInlineIf. phpcs went green and phpmd went red:

  lib/Controller/ZakenController.php:264  ElseExpression  The method destroy
  uses an else expression. Else clauses are basically not necessary and you
  can simplify the code by not using them.

16 of those, and phpmd was SUCCESS on the previous run (31586148039) — so
this was mine, not pre-existing. Two gates in the same pipeline disagreed
about the shape of a two-way choice and I only checked one of them before
pushing.

Rewritten as assign-default-then-override, or as an early `continue` where
the branch sits in a loop. Same outcomes, same evaluation order, no ternary
and no else.

Measured on php 8.3.33 with this branch's vendor tree, all six gates that
this commit could plausibly touch:
  phpmd        16 violations -> 0 (both phpmd.xml and phpmd-unusedparams.xml)
  phpcs        0 errors
  php-cs-fixer 0 of 88 files to fix
  PHPUnit      OK (268 tests, 825 assertions)
  psalm        0 errors
  phpstan      No errors

* fix(quality): restore the #201 deviation in the stub instead of paying the debt here

The migration to the shared semantics-only ruleset dropped this app's documented
exemption. Its previous phpcs.xml demoted ten commenting and named-parameter
sniffs from errors to warnings, with #201 tracking
the cleanup; the stub replaced that wholesale and the app went red on debt it had
been explicitly excused from.

Reverts the two commits that tried to fix it the other way — paying the debt off
inside this PR by rewriting docblocks and ternaries across the controllers, then
removing the sixteen else branches those rewrites introduced. That work is #201's
and it does not belong here: it puts real behaviour changes into a pull request
whose central claim is that it changes no behaviour, and the diff stops being
reviewable at exactly the moment reviewing it matters.

The shared stub was designed to carry a deviation like this — "App-specific
deviations go BELOW this line, each with a reason and a tracking issue" — so it
is restored there, named and tracked, rather than suppressed centrally or paid
off opportunistically. Structural rules still gate the build as errors: spacing,
control structures, banned debug functions, and the removed-Nextcloud-API sniff.

gate-65 permits this. It fails an app for declaring FORMATTING sniffs locally, or
for pinning a shared package — not for a reasoned severity change on a semantic
rule, which is exactly the escape hatch that keeps a shared standard adoptable.

* style(stylelint): drop two empty <style> blocks the widened glob now checks

The config commit quoted the stylelint glob. Unquoted, `src/**/*.vue` was
expanded by the shell (no globstar) to a single level, so nothing under
src/modals/<subdir>/ had ever been linted. Quoting it hands expansion to
stylelint, which walks the tree and found two no-empty-source violations.

Both files are untouched by this PR and carry the same empty block on
development, so this is pre-existing debt the wider check exposed rather than
anything the reformat introduced. An empty, non-scoped <style> block emits no
CSS, so removing it is behaviour-neutral: 6 deletions, 0 insertions.

no-empty-source is not auto-fixable, so this is a hand fix. The glob stays
quoted.
rubenvdlinde added a commit to ConductionNL/openconnector that referenced this pull request Aug 12, 2026
TWO DEFECTS, both of which made a job red rather than a finding.

1. composer.lock locked packages this app's own platform cannot run.

`config.platform.php` is pinned to "8.3", and the lock carried
symfony/string v8.1.2, symfony/finder v8.1.1 and symfony/process v8.1.0 —
all three require php >=8.4.1. That is the signature of a regeneration run
with a blanket `--ignore-platform-reqs`: the flag suppresses the PHP version
constraint as well as the extension ones, so composer resolves against a
platform that does not exist here and writes the result down as if it did.

`composer validate` passes on such a lock — the content-hash still matches
composer.json — so the defect is invisible to the check most likely to be
run, and only surfaces at install time. In CI it took out NINE jobs, all
with the same line and none of them naming a package this app requires
directly:

  Your lock file does not contain a compatible set of packages.

  PHP Quality (lint / phpcs / phpmd / phpstan / psalm / phpmetrics)
  Security (composer), License (composer)
  PHPUnit (8.3 and 8.4), Integration Tests (Newman), E2E (Playwright)

and it cascaded one gate further: gate-33 axe-core reported SKIPPED
(structural) because the Playwright job that produces its report never ran.

Re-resolved with the three packages named explicitly and `--ignore-platform-
req=ext-*` — extension constraints only, so the PHP version stays enforced.
They downgrade to the 7.4 line, which is where the rest of the symfony
packages here already are:

  symfony/finder   v8.1.1 -> v7.4.14
  symfony/process  v8.1.0 -> v7.4.13
  symfony/string   v8.1.2 -> v7.4.15

`nextcloud/ocp` is unaffected and still resolves to v34.0.2, which is the
point of this PR. `composer install --dry-run` under the pinned 8.3 platform
now completes, and a sweep of every locked package's `require.php` finds
zero above 8.3 — the same sweep over the previous lock finds exactly those
three, so it is a measurement and not a hopeful assertion.

2. appinfo/info.xml did not validate against the App Store schema.

`info.xml lint` landed in the shared workflow today
(ConductionNL/.github#383) and this is the first run that had it. The App
Store's info.xsd declares repair-steps as an xs:sequence — pre-migration,
post-migration, live-migration, install, uninstall — so `<install>` must
come AFTER `<post-migration>`, which is the opposite of the order they run
in and the opposite of how #1224 wrote them:

  appinfo/info.xml:115: element post-migration: Schemas validity error :
  Element 'post-migration': This element is not expected.
  Expected is ( uninstall ).

Swapped, with the constraint written down next to it so the intuitive order
does not come back. Nothing about which hooks run, or when, changes.

⚠️ THE BASE BRANCH FAILS THIS CHECK TOO, and earlier: `origin/development`'s
info.xml is rejected at line 28 with `Element 'category': This element is
not expected. Expected is one of ( licence, author )`. This branch already
fixed that half in d960a2f. Verified by running xmllint against the real
appstore info.xsd over both files — head validates, base does not.
rubenvdlinde added a commit to ConductionNL/portaliq that referenced this pull request Aug 12, 2026
* chore: adopt nextcloud/coding-standard, .editorconfig and NC 34

Configuration only. The reformat is the next commit on purpose, so
.git-blame-ignore-revs can name a revision containing nothing but whitespace.

- .php-cs-fixer.dist.php + conduction/coding-standard, which extends
  nextcloud/coding-standard and can only ADD to it — enforced by that package's
  invariant test, not by review.
- cs:check / cs:fix now run php-cs-fixer. They were aliases for phpcs/phpcbf,
  so the documented Nextcloud command reformatted code AWAY from Nextcloud's
  standard.
- nextcloud/coding-standard dropped as a direct dependency. It arrives
  transitively at a version conduction/coding-standard has tested against;
  declared directly it was a dead dependency with no config and no invocation.
- phpcs.xml is now a stub over the shared semantics-only ruleset, and the local
  phpcs-custom-sniffs/ copy is gone. The fleet was carrying six divergent
  versions of NamedParametersSniff.php — a custom RULE, not a setting.
- .editorconfig, verbatim from nextcloud/server. No fleet app had one, so an
  editor configured by someone's previous Nextcloud work defaulted to tabs,
  which the old ruleset then rejected.
- nextcloud/ocp -> ^34.0 and PHPUnit -> stable34. This app declared support for
  NC 34 while being analysed against 31, so a symbol REMOVED in 32/33/34 was
  invisible to the type checker. That is why the NC 34 removal of \OC::$server
  needed a hand-written PHPCS sniff.
- the stylelint glob is quoted, so stylelint expands it rather than the shell.
  Unquoted, src/**/ matches exactly one directory level and nested components
  are silently unlinted.

gate-65 (coding-standard-adoption) enforces all of the above from
ConductionNL/.github@main. This app failed it; with this commit it passes.

* style: reformat with nextcloud/coding-standard — whitespace only

Applied by php-cs-fixer with conduction/coding-standard. Tabs, same-line braces,
(int)$x, single-space concatenation, ordered imports — Nextcloud's dialect, which
this app now passes unchanged. 96 file(s), no behaviour change.

Isolated from the configuration change so .git-blame-ignore-revs can name a
revision that touches nothing but formatting. Reviewing it line by line is not a
useful activity; the previous commit is the review.

* chore: ignore the reformat commit in git blame

9a97095 touches 96 files and changes no behaviour. Without this, every line it
reflowed attributes to it and the real author is one --skip away.

GitHub honours the file automatically; locally it needs
`git config blame.ignoreRevsFile .git-blame-ignore-revs` once.

* fix: regenerate composer.lock for the new constraints

The previous commit changed composer.json without touching the lock, so
`composer install` refused with exit 4 and EVERY PHP job failed:

    Required (in require-dev) package "conduction/coding-standard" is not
    present in the lock file.
    Required (in require-dev) package "conduction/hydra-gates" is not present
    in the lock file.
    Required (in require-dev) package "nextcloud/ocp" is in the lock file as
    "v31.0.9" but that does not satisfy your constraint "^34.0".

Nothing was wrong with the reformat or the ruleset — the jobs never got as far
as running a tool. Measured on larpingapp#313 before this fix: phpcs, psalm,
phpstan and both PHPUnit legs red, all of them at `composer install`. Hydra
Gates passed in the same run, because it does not install composer
dependencies.

Now locked at conduction/coding-standard v1.0.0, conduction/hydra-gates v1.7.0,
nextcloud/ocp v34.0.2 — the last of which is the point of the exercise: this app
declares support for NC 34 and is now analysed against it.

* fix(appinfo): order info.xml elements per the App Store xs:sequence

The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. Nothing was added, removed or
reworded; <version> and the <nextcloud> min/max-version declaration are
unchanged.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.

* fix(ci): adopt NC 34 by ADDING it to the matrix, and unpin the admin-nav selector

The config commit replaced `nextcloud-test-refs` with `["stable34"]`. That
turned three jobs red, all three green on development (run 31574689088), so all
three are this change's doing. It also silently dropped the stable32 and
stable33 legs development had.

1. PHPUnit — AppInfoDependenciesTest::testFloorIsNotBelowTheOldestTestedVersion
   failed: "declares min-version=32 but the oldest Nextcloud this repo tests is
   34 ... Either raise min-version to 34 or add the missing leg(s) to the
   matrix." Took the second option, because the first is a support-policy change
   and this is a formatting chore. The matrix is now
   ["stable34", "stable32", "stable33"] — every major inside the declared 32-34
   range, which also satisfies testEveryTestedVersionIsInsideTheDeclaredRange
   from the other side.

2. E2E — "SettingsSection must contribute an entry to the admin navigation" was
   never true. The section registers fine; the locator was scoped to
   `#app-navigation`, which NC 34 does not render. Verified against
   nextcloud/server: apps/settings/templates/settings/frame.php is
   `<div id="app-navigation">` on stable32 and stable33, and on stable34 the
   same file is `<div id="settings-app"></div>` — the settings page became a Vue
   app, and apps/settings/src/views/SettingsNavigation.vue renders the list via
   `<NcAppNavigation>`, whose root is `id="app-navigation-vue"`. The selector now
   accepts either container, so it is honest on all three legs rather than on
   the two it happened to be written against. This is exactly why stable34 is
   first in the list: [0] is the server playwright gets.

Not fixed here, on purpose:

- gate-16 flags WooController::serve and ::servePath as changed methods missing
  @SPEC. They are pre-existing debt, not a regression: `git show
  origin/development:lib/Controller/WooController.php` has zero @SPEC tags in
  the file, and so does HEAD — the reformat only alphabetised one import and
  moved a brace (`git diff -w` on that file shows nothing else), which is enough
  to pull two long-untagged methods into a diff-scoped gate. gate-16 cannot be
  run on development for comparison — it reports NOT APPLICABLE with no diff by
  design (ADR-020) — so the base-tree read is the available evidence. Tagging
  them would be paying legacy debt inside a whitespace PR.

- gate-48 is a known false positive fixed upstream in ConductionNL/.github#388
  (merged, b745bf2), which apps pick up automatically via the floating
  hydra-gates ref. Measured here too: NoCSRFRequired count is 32 on
  origin/development and 32 on HEAD, identical per file, and the diff is 21
  removals against 21 identical additions plus 6 against 6 in docblocks — every
  one a four-spaces-to-tab re-indent. No waiver added.
rubenvdlinde added a commit to ConductionNL/opencatalogi that referenced this pull request Aug 12, 2026
* chore: adopt nextcloud/coding-standard, .editorconfig and NC 34

Configuration only. The reformat is the next commit on purpose, so
.git-blame-ignore-revs can name a revision containing nothing but whitespace.

- .php-cs-fixer.dist.php + conduction/coding-standard, which extends
  nextcloud/coding-standard and can only ADD to it — enforced by that package's
  invariant test, not by review.
- cs:check / cs:fix now run php-cs-fixer. They were aliases for phpcs/phpcbf,
  so the documented Nextcloud command reformatted code AWAY from Nextcloud's
  standard.
- nextcloud/coding-standard dropped as a direct dependency. It arrives
  transitively at a version conduction/coding-standard has tested against;
  declared directly it was a dead dependency with no config and no invocation.
- phpcs.xml is now a stub over the shared semantics-only ruleset, and the local
  phpcs-custom-sniffs/ copy is gone. The fleet was carrying six divergent
  versions of NamedParametersSniff.php — a custom RULE, not a setting.
- .editorconfig, verbatim from nextcloud/server. No fleet app had one, so an
  editor configured by someone's previous Nextcloud work defaulted to tabs,
  which the old ruleset then rejected.
- nextcloud/ocp -> ^34.0 and PHPUnit -> stable34. This app declared support for
  NC 34 while being analysed against 31, so a symbol REMOVED in 32/33/34 was
  invisible to the type checker. That is why the NC 34 removal of \OC::$server
  needed a hand-written PHPCS sniff.
- the stylelint glob is quoted, so stylelint expands it rather than the shell.
  Unquoted, src/**/ matches exactly one directory level and nested components
  are silently unlinted.

gate-65 (coding-standard-adoption) enforces all of the above from
ConductionNL/.github@main. This app failed it; with this commit it passes.

* style: reformat with nextcloud/coding-standard — whitespace only

Applied by php-cs-fixer with conduction/coding-standard. Tabs, same-line braces,
(int)$x, single-space concatenation, ordered imports — Nextcloud's dialect, which
this app now passes unchanged. 195 file(s), no behaviour change.

Isolated from the configuration change so .git-blame-ignore-revs can name a
revision that touches nothing but formatting. Reviewing it line by line is not a
useful activity; the previous commit is the review.

* chore: ignore the reformat commit in git blame

6a93c2c touches 195 files and changes no behaviour. Without this, every line it
reflowed attributes to it and the real author is one --skip away.

GitHub honours the file automatically; locally it needs
`git config blame.ignoreRevsFile .git-blame-ignore-revs` once.

* fix: regenerate composer.lock for the new constraints

The previous commit changed composer.json without touching the lock, so
`composer install` refused with exit 4 and EVERY PHP job failed:

    Required (in require-dev) package "conduction/coding-standard" is not
    present in the lock file.
    Required (in require-dev) package "conduction/hydra-gates" is not present
    in the lock file.
    Required (in require-dev) package "nextcloud/ocp" is in the lock file as
    "v31.0.9" but that does not satisfy your constraint "^34.0".

Nothing was wrong with the reformat or the ruleset — the jobs never got as far
as running a tool. Measured on larpingapp#313 before this fix: phpcs, psalm,
phpstan and both PHPUnit legs red, all of them at `composer install`. Hydra
Gates passed in the same run, because it does not install composer
dependencies.

Now locked at conduction/coding-standard v1.0.0, conduction/hydra-gates v1.7.0,
nextcloud/ocp v34.0.2 — the last of which is the point of the exercise: this app
declares support for NC 34 and is now analysed against it.

* fix(appinfo): order info.xml elements per the App Store xs:sequence

The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. Nothing was added, removed or
reworded; <version> and the <nextcloud> min/max-version declaration are
unchanged.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.

* style(stylelint): clear the lint debt the widened glob newly exposes

The config commit quoted the previously-unquoted stylelint glob. Unquoted,
/bin/sh (dash) expanded 'src/**/*.vue' as 'src/*/*.vue' — one directory deep —
so every component nested two levels under src/ was silently never linted.
Quoting it hands the pattern to stylelint, whose own glob engine recurses.

These violations are pre-existing debt in newly-covered files, not regressions:
every offending file sits at src/<a>/<b>/<file>.vue and was matched zero times
by the old expansion. Fixed with stylelint --fix (rule-empty-line-before) plus
one hand-removed verbatim duplicate selector where --fix could not.

No hardcoded colours introduced; the glob is left widened.

* ci: re-run against the repaired shared quality.yml

The previous run hit a startup failure: quality.yml@main carried a duplicate
'name:' key and a job with no steps, spliced in by .github#385. Repaired by
.github#389. A startup-failed run cannot be re-run, so this empty commit
requests a fresh evaluation.

* fix: drop an accidentally committed node_modules symlink

Local tooling symlink picked up by 'git add -A'. It escaped .gitignore because
the rule is 'node_modules/' with a trailing slash, which matches a DIRECTORY —
a symlink of the same name is a blob (mode 120000), not a directory, so the
rule never applied. It pointed at an absolute path outside the repo.
rubenvdlinde added a commit to ConductionNL/softwarecatalog that referenced this pull request Aug 12, 2026
* chore: adopt nextcloud/coding-standard, .editorconfig and NC 34

Configuration only. The reformat is the next commit on purpose, so
.git-blame-ignore-revs can name a revision containing nothing but whitespace.

- .php-cs-fixer.dist.php + conduction/coding-standard, which extends
  nextcloud/coding-standard and can only ADD to it — enforced by that package's
  invariant test, not by review.
- cs:check / cs:fix now run php-cs-fixer. They were aliases for phpcs/phpcbf,
  so the documented Nextcloud command reformatted code AWAY from Nextcloud's
  standard.
- nextcloud/coding-standard dropped as a direct dependency. It arrives
  transitively at a version conduction/coding-standard has tested against;
  declared directly it was a dead dependency with no config and no invocation.
- phpcs.xml is now a stub over the shared semantics-only ruleset, and the local
  phpcs-custom-sniffs/ copy is gone. The fleet was carrying six divergent
  versions of NamedParametersSniff.php — a custom RULE, not a setting.
- .editorconfig, verbatim from nextcloud/server. No fleet app had one, so an
  editor configured by someone's previous Nextcloud work defaulted to tabs,
  which the old ruleset then rejected.
- nextcloud/ocp -> ^34.0 and PHPUnit -> stable34. This app declared support for
  NC 34 while being analysed against 31, so a symbol REMOVED in 32/33/34 was
  invisible to the type checker. That is why the NC 34 removal of \OC::$server
  needed a hand-written PHPCS sniff.
- the stylelint glob is quoted, so stylelint expands it rather than the shell.
  Unquoted, src/**/ matches exactly one directory level and nested components
  are silently unlinted.

gate-65 (coding-standard-adoption) enforces all of the above from
ConductionNL/.github@main. This app failed it; with this commit it passes.

* style: reformat with nextcloud/coding-standard — whitespace only

Applied by php-cs-fixer with conduction/coding-standard. Tabs, same-line braces,
(int)$x, single-space concatenation, ordered imports — Nextcloud's dialect, which
this app now passes unchanged. 210 file(s), no behaviour change.

Isolated from the configuration change so .git-blame-ignore-revs can name a
revision that touches nothing but formatting. Reviewing it line by line is not a
useful activity; the previous commit is the review.

* chore: ignore the reformat commit in git blame

a78e00a touches 210 files and changes no behaviour. Without this, every line it
reflowed attributes to it and the real author is one --skip away.

GitHub honours the file automatically; locally it needs
`git config blame.ignoreRevsFile .git-blame-ignore-revs` once.

* fix: regenerate composer.lock for the new constraints

The previous commit changed composer.json without touching the lock, so
`composer install` refused with exit 4 and EVERY PHP job failed:

    Required (in require-dev) package "conduction/coding-standard" is not
    present in the lock file.
    Required (in require-dev) package "conduction/hydra-gates" is not present
    in the lock file.
    Required (in require-dev) package "nextcloud/ocp" is in the lock file as
    "v31.0.9" but that does not satisfy your constraint "^34.0".

Nothing was wrong with the reformat or the ruleset — the jobs never got as far
as running a tool. Measured on larpingapp#313 before this fix: phpcs, psalm,
phpstan and both PHPUnit legs red, all of them at `composer install`. Hydra
Gates passed in the same run, because it does not install composer
dependencies.

Now locked at conduction/coding-standard v1.0.0, conduction/hydra-gates v1.7.0,
nextcloud/ocp v34.0.2 — the last of which is the point of the exercise: this app
declares support for NC 34 and is now analysed against it.

* fix(appinfo): order info.xml elements per the App Store xs:sequence

The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. Nothing was added, removed or
reworded; <version> and the <nextcloud> min/max-version declaration are
unchanged.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.

* fix(static-analysis): repair what the nextcloud/ocp 31 -> 34 bump and the elseif normalisation surfaced

PHPStan (5 errors), all from the OCP 31 -> 34 stub change:
- IQueryBuilder::execute() is gone from the OCP 34 interface. The four
  call sites in OrganizationSyncService are all SELECTs, so they become
  executeQuery(); no behaviour change.
- TemplateResponse's 4th constructor argument is $renderAs (a string
  enum), not the HTTP status; the 5th is int $status. The error branch
  in DashboardController passed '500' as $renderAs, so it rendered with
  an invalid layout and still returned HTTP 200. It now passes
  RENDER_AS_ERROR plus STATUS_INTERNAL_SERVER_ERROR.

Psalm (2 ParadoxicalCondition errors): extractPropertyDefinitionMap in
ArchiMateService and ArchiMateImportService each end with an elseif that
repeats the opening if verbatim, so the third branch is unreachable. The
duplicate is pre-existing (origin/development ArchiMateService.php:2437);
what changed is that php-cs-fixer rewrote 'else if' to 'elseif', and
Psalm reports the elseif form as ParadoxicalCondition but the 'else if'
form as NoValue -- and psalm.xml suppresses NoValue. Verified with a
two-file control. Removing the unreachable branch is behaviour-identical.

* ci: re-trigger Code Quality

The previous run produced zero jobs and concluded failure: it started
inside the window where ConductionNL/.github@main carried the broken
quality.yml splice from b745bf2f, repaired at 4118bca8. Nothing in this
PR touches the workflow.
rubenvdlinde added a commit to ConductionNL/hermiq that referenced this pull request Aug 12, 2026
* chore: adopt nextcloud/coding-standard, .editorconfig and NC 34

Configuration only. The reformat is the next commit on purpose, so
.git-blame-ignore-revs can name a revision containing nothing but whitespace.

- .php-cs-fixer.dist.php + conduction/coding-standard, which extends
  nextcloud/coding-standard and can only ADD to it — enforced by that package's
  invariant test, not by review.
- cs:check / cs:fix now run php-cs-fixer. They were aliases for phpcs/phpcbf,
  so the documented Nextcloud command reformatted code AWAY from Nextcloud's
  standard.
- nextcloud/coding-standard dropped as a direct dependency. It arrives
  transitively at a version conduction/coding-standard has tested against;
  declared directly it was a dead dependency with no config and no invocation.
- phpcs.xml is now a stub over the shared semantics-only ruleset, and the local
  phpcs-custom-sniffs/ copy is gone. The fleet was carrying six divergent
  versions of NamedParametersSniff.php — a custom RULE, not a setting.
- .editorconfig, verbatim from nextcloud/server. No fleet app had one, so an
  editor configured by someone's previous Nextcloud work defaulted to tabs,
  which the old ruleset then rejected.
- nextcloud/ocp -> ^34.0 and PHPUnit -> stable34. This app declared support for
  NC 34 while being analysed against 31, so a symbol REMOVED in 32/33/34 was
  invisible to the type checker. That is why the NC 34 removal of \OC::$server
  needed a hand-written PHPCS sniff.
- the stylelint glob is quoted, so stylelint expands it rather than the shell.
  Unquoted, src/**/ matches exactly one directory level and nested components
  are silently unlinted.

gate-65 (coding-standard-adoption) enforces all of the above from
ConductionNL/.github@main. This app failed it; with this commit it passes.

* style: reformat with nextcloud/coding-standard — whitespace only

Applied by php-cs-fixer with conduction/coding-standard. Tabs, same-line braces,
(int)$x, single-space concatenation, ordered imports — Nextcloud's dialect, which
this app now passes unchanged. 397 file(s), no behaviour change.

Isolated from the configuration change so .git-blame-ignore-revs can name a
revision that touches nothing but formatting. Reviewing it line by line is not a
useful activity; the previous commit is the review.

* chore: ignore the reformat commit in git blame

b2af4be touches 397 files and changes no behaviour. Without this, every line it
reflowed attributes to it and the real author is one --skip away.

GitHub honours the file automatically; locally it needs
`git config blame.ignoreRevsFile .git-blame-ignore-revs` once.

* fix: regenerate composer.lock for the new constraints

The previous commit changed composer.json without touching the lock, so
`composer install` refused with exit 4 and EVERY PHP job failed:

    Required (in require-dev) package "conduction/coding-standard" is not
    present in the lock file.
    Required (in require-dev) package "conduction/hydra-gates" is not present
    in the lock file.
    Required (in require-dev) package "nextcloud/ocp" is in the lock file as
    "v31.0.9" but that does not satisfy your constraint "^34.0".

Nothing was wrong with the reformat or the ruleset — the jobs never got as far
as running a tool. Measured on larpingapp#313 before this fix: phpcs, psalm,
phpstan and both PHPUnit legs red, all of them at `composer install`. Hydra
Gates passed in the same run, because it does not install composer
dependencies.

Now locked at conduction/coding-standard v1.0.0, conduction/hydra-gates v1.7.0,
nextcloud/ocp v34.0.2 — the last of which is the point of the exercise: this app
declares support for NC 34 and is now analysed against it.

* fix(quality): clear the 15 phpcs errors this branch's semantics-only ruleset surfaces

composer install succeeds here, so unlike the other apps in this migration
the phpcs job actually reached the tool and reported real findings:

  FOUND 10 ERRORS ... PEAR.Commenting.FunctionComment.ParamCommentAlignmentExceeded
  Line exceeds maximum limit of 150 characters; contains 160 / 153 / 210 / 182 / 182

Two distinct causes.

1. Five over-length lines, all the same shape: a long @return type whose
   description continues on the next line, aligned past column 150. The
   descriptions move into the docblock body. Nothing about the contract
   changes; the text stops being pushed off the right edge.

2. Ten ParamCommentAlignmentExceeded on RunTokenService::mint(). The shared
   ruleset switches PEAR's alignment codes off individually — including
   ParamCommentAlignment — because alignment is php-cs-fixer's job now. It
   does not switch off the sibling code PEAR emits when the comment is
   aligned too FAR right: ParamCommentAlignmentExceeded. This block's
   continuation lines sit at 33 spaces where the sniff computes 29, so the
   only app in the fleet with a wide param-comment block is the only one
   that trips it. Re-indented to 29. The gap in the shared ruleset is
   reported separately; this app does not work around it locally.

Measured locally on php 8.3.33 with the branch's own vendor tree:
`phpcs --standard=phpcs.xml` exits 0 (3 @SPEC warnings remain, which
ignore_warnings_on_exit deliberately does not fail on), and
`php-cs-fixer fix --dry-run` finds 0 of 396 files to fix — so the
re-indent is stable and the two tools are not arguing.

* fix(appinfo): order info.xml elements per the App Store xs:sequence

The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. Nothing was added, removed or
reworded; <version> and the <nextcloud> min/max-version declaration are
unchanged.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.

* fix: repair the analysis stub and widen-exposed stylelint findings

PHPStan (12 errors). OCP 34 rewrote OCP\Server::get()'s docblock: v31.0.9
carried a plain '@return T|mixed', which PHPStan resolves to mixed, so
Server::get(self::BROKER_CLASS) produced an untyped object. v34.0.2 drops
the plain @return and keeps only the conditional
'@psalm-return ($serviceName is class-string<T> ? T : mixed)', so PHPStan
now resolves the literal class-string constant to the real class and reads
our own tests/Stubs CredentialBrokerService. Two stub-fidelity gaps then
surfaced:
- request() promised array{status: int, headers: ..., body: string} with
  all keys required, making the callers' '?? ' fallbacks look redundant.
  The keys are now optional. The broker is a SOFT boundary reached through
  a class_exists() probe, so hermiq runs against whatever openregister
  version is installed and cannot promise a key is present; the callers
  defend each one, and the stub should not contradict them.
- resolveInjectable() was missing entirely. Added with the signature
  ProviderFactory::resolveCliToken() already documents (string|null, where
  null is a routing signal rather than a denial).

No application code changed; both fixes are in the analysis stub.

stylelint: 16 rule-empty-line-before errors, all auto-fixed. They were
never linted before because the glob was unquoted, so the shell expanded
'src/**/*.vue' to 'src/*/*.vue' and never reached
src/components/<dir>/<file>.vue. The check script was quoted in the config
commit; the stylelint-fix script is now quoted to match, so fix and check
cover the same set.
rubenvdlinde added a commit to ConductionNL/nldesign that referenced this pull request Aug 12, 2026
* chore: adopt nextcloud/coding-standard, .editorconfig and NC 34

Configuration only. The reformat is the next commit on purpose, so
.git-blame-ignore-revs can name a revision containing nothing but whitespace.

- .php-cs-fixer.dist.php + conduction/coding-standard, which extends
  nextcloud/coding-standard and can only ADD to it — enforced by that package's
  invariant test, not by review.
- cs:check / cs:fix now run php-cs-fixer. They were aliases for phpcs/phpcbf,
  so the documented Nextcloud command reformatted code AWAY from Nextcloud's
  standard.
- nextcloud/coding-standard dropped as a direct dependency. It arrives
  transitively at a version conduction/coding-standard has tested against;
  declared directly it was a dead dependency with no config and no invocation.
- phpcs.xml is now a stub over the shared semantics-only ruleset, and the local
  phpcs-custom-sniffs/ copy is gone. The fleet was carrying six divergent
  versions of NamedParametersSniff.php — a custom RULE, not a setting.
- .editorconfig, verbatim from nextcloud/server. No fleet app had one, so an
  editor configured by someone's previous Nextcloud work defaulted to tabs,
  which the old ruleset then rejected.
- nextcloud/ocp -> ^34.0 and PHPUnit -> stable34. This app declared support for
  NC 34 while being analysed against 31, so a symbol REMOVED in 32/33/34 was
  invisible to the type checker. That is why the NC 34 removal of \OC::$server
  needed a hand-written PHPCS sniff.
- the stylelint glob is quoted, so stylelint expands it rather than the shell.
  Unquoted, src/**/ matches exactly one directory level and nested components
  are silently unlinted.

gate-65 (coding-standard-adoption) enforces all of the above from
ConductionNL/.github@main. This app failed it; with this commit it passes.

* style: reformat with nextcloud/coding-standard — whitespace only

Applied by php-cs-fixer with conduction/coding-standard. Tabs, same-line braces,
(int)$x, single-space concatenation, ordered imports — Nextcloud's dialect, which
this app now passes unchanged. 128 file(s), no behaviour change.

Isolated from the configuration change so .git-blame-ignore-revs can name a
revision that touches nothing but formatting. Reviewing it line by line is not a
useful activity; the previous commit is the review.

* chore: ignore the reformat commit in git blame

8c0f6b8 touches 128 files and changes no behaviour. Without this, every line it
reflowed attributes to it and the real author is one --skip away.

GitHub honours the file automatically; locally it needs
`git config blame.ignoreRevsFile .git-blame-ignore-revs` once.

* fix: regenerate composer.lock for the new constraints

The previous commit changed composer.json without touching the lock, so
`composer install` refused with exit 4 and EVERY PHP job failed:

    Required (in require-dev) package "conduction/coding-standard" is not
    present in the lock file.
    Required (in require-dev) package "conduction/hydra-gates" is not present
    in the lock file.
    Required (in require-dev) package "nextcloud/ocp" is in the lock file as
    "v31.0.9" but that does not satisfy your constraint "^34.0".

Nothing was wrong with the reformat or the ruleset — the jobs never got as far
as running a tool. Measured on larpingapp#313 before this fix: phpcs, psalm,
phpstan and both PHPUnit legs red, all of them at `composer install`. Hydra
Gates passed in the same run, because it does not install composer
dependencies.

Now locked at conduction/coding-standard v1.0.0, conduction/hydra-gates v1.7.0,
nextcloud/ocp v34.0.2 — the last of which is the point of the exercise: this app
declares support for NC 34 and is now analysed against it.

* fix(appinfo): order info.xml elements per the App Store xs:sequence

The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. Nothing was added, removed or
reworded; <version> and the <nextcloud> min/max-version declaration are
unchanged.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.

* fix(ci): make the NC 34 move honest — test both ends of the declared range

The config commit moved `nextcloud-test-refs` to `["stable34"]` and pointed
stylelint at a directory this app does not have. Five jobs went red, all five
green on development (run 31573122447), so all five are this change's doing.

1. PHPUnit — ClaimAccuracyTest::testDeclaredNextcloudRangeMatchesTheTestedMatrix
   failed: "declares min-version=32, but the lowest ref CI tests is stable34".
   That guard exists because this attribute already drifted from the matrix
   twice (#241/#242). Resolved by testing the range rather than shrinking it:
   `["stable34", "stable32"]`. The stable34 leg is the only thing that can catch
   a symbol REMOVED in 33/34 — the stated reason for this migration — and the
   stable32 leg is the only thing that can catch a symbol that does not exist
   yet at the declared floor. Raising the floor to 34 instead would have been a
   support-policy change smuggled into a formatting chore, and it would have
   falsified six ALLOWED reasons in selector-liveness that read "kept for older
   servers".

2. E2E — selector-liveness reported eight selectors matching nothing. Not a
   flake and not a regression in the CSS: stable34 is the FIRST survey this spec
   has ever taken above NC 32, and NC 32/33 header markup (app-menu-entry__*,
   app-menu-icon, unified-search-menu — several added by #260 one commit ago)
   genuinely does not exist there. They are the mirror image of the SINCE list,
   which meanwhile did its job perfectly: every selector it deferred came back
   live on 34, so the expiry assertion passed unattended. Recorded in ALLOWED
   with both measurements behind them, and the file's stale "CI pins
   ["stable32"]" note corrected.

3. stylelint — the glob became `src/**/*.{vue,scss,css}`, which is a template
   from an app with Vue components. nldesign has no `src` CSS at all (`lint` is
   literally `echo 'No JavaScript to lint - nldesign is CSS/PHP only'`), so
   stylelint exited 1 with NoFilesFoundError and linted nothing. Now
   `"css/**/*.{css,scss}"` — quoted, so stylelint expands it rather than the
   shell, and verified a superset: 110 files under the old
   `'css/**/*.css'`, the same 110 under the new one, none dropped.

Verified locally: the shipped glob reproduces NoFilesFoundError and the new one
exits 0; the two claim-accuracy assertions now hold (min 32 == lowest 32,
max 34 >= highest 34); vitest 81/81; tsc reports nothing in the edited spec.

appinfo/info.xml keeps min-version=32 — only its prose is corrected, since it
claimed CI pinned stable32.
rubenvdlinde added a commit to ConductionNL/launchpad that referenced this pull request Aug 12, 2026
* chore: adopt nextcloud/coding-standard, .editorconfig and NC 34

Configuration only. The reformat is the next commit on purpose, so
.git-blame-ignore-revs can name a revision containing nothing but whitespace.

- .php-cs-fixer.dist.php + conduction/coding-standard, which extends
  nextcloud/coding-standard and can only ADD to it — enforced by that package's
  invariant test, not by review.
- cs:check / cs:fix now run php-cs-fixer. They were aliases for phpcs/phpcbf,
  so the documented Nextcloud command reformatted code AWAY from Nextcloud's
  standard.
- nextcloud/coding-standard dropped as a direct dependency. It arrives
  transitively at a version conduction/coding-standard has tested against;
  declared directly it was a dead dependency with no config and no invocation.
- phpcs.xml is now a stub over the shared semantics-only ruleset, and the local
  phpcs-custom-sniffs/ copy is gone. The fleet was carrying six divergent
  versions of NamedParametersSniff.php — a custom RULE, not a setting.
- .editorconfig, verbatim from nextcloud/server. No fleet app had one, so an
  editor configured by someone's previous Nextcloud work defaulted to tabs,
  which the old ruleset then rejected.
- nextcloud/ocp -> ^34.0 and PHPUnit -> stable34. This app declared support for
  NC 34 while being analysed against 31, so a symbol REMOVED in 32/33/34 was
  invisible to the type checker. That is why the NC 34 removal of \OC::$server
  needed a hand-written PHPCS sniff.
- the stylelint glob is quoted, so stylelint expands it rather than the shell.
  Unquoted, src/**/ matches exactly one directory level and nested components
  are silently unlinted.

gate-65 (coding-standard-adoption) enforces all of the above from
ConductionNL/.github@main. This app failed it; with this commit it passes.

* style: reformat with nextcloud/coding-standard — whitespace only

Applied by php-cs-fixer with conduction/coding-standard. Tabs, same-line braces,
(int)$x, single-space concatenation, ordered imports — Nextcloud's dialect, which
this app now passes unchanged. 476 file(s), no behaviour change.

Isolated from the configuration change so .git-blame-ignore-revs can name a
revision that touches nothing but formatting. Reviewing it line by line is not a
useful activity; the previous commit is the review.

* chore: ignore the reformat commit in git blame

46e028e touches 476 files and changes no behaviour. Without this, every line it
reflowed attributes to it and the real author is one --skip away.

GitHub honours the file automatically; locally it needs
`git config blame.ignoreRevsFile .git-blame-ignore-revs` once.

* fix: regenerate composer.lock for the new constraints

The previous commit changed composer.json without touching the lock, so
`composer install` refused with exit 4 and EVERY PHP job failed:

    Required (in require-dev) package "conduction/coding-standard" is not
    present in the lock file.
    Required (in require-dev) package "conduction/hydra-gates" is not present
    in the lock file.
    Required (in require-dev) package "nextcloud/ocp" is in the lock file as
    "v31.0.9" but that does not satisfy your constraint "^34.0".

Nothing was wrong with the reformat or the ruleset — the jobs never got as far
as running a tool. Measured on larpingapp#313 before this fix: phpcs, psalm,
phpstan and both PHPUnit legs red, all of them at `composer install`. Hydra
Gates passed in the same run, because it does not install composer
dependencies.

Now locked at conduction/coding-standard v1.0.0, conduction/hydra-gates v1.7.0,
nextcloud/ocp v34.0.2 — the last of which is the point of the exercise: this app
declares support for NC 34 and is now analysed against it.

* fix(appinfo): order info.xml elements per the App Store xs:sequence

The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. <version> and the <nextcloud>
min/max-version declaration are unchanged.

<commands> now precedes <settings>, and <navigations> follows <activity>;
inside <repair-steps>, <post-migration> precedes <install> (the schema's order
is pre-migration, post-migration, live-migration, install, uninstall - it does
not reflect execution order, which Nextcloud selects by event).

Two content fixes were unavoidable, as neither is an ordering problem.

<types><search/></types> is not a valid app type at any position: the schema
allows only prelogin, filesystem, authentication, extended_authentication,
logging, dav, prevent_group_restriction and session, and Nextcloud does
nothing with an unrecognised one. Unified search is already wired properly -
Application.php calls registerSearchProvider(LaunchPadSearchProvider) - so the
element was decorative. Replaced by a comment pointing at that registration.

<activity><provider> had to be wrapped in <providers>. The direct child is
what the schema rejects, and Nextcloud reads the provider list from
$info['activity']['providers']['provider'], so the Activity provider
(REQ-ACT-001) was in fact not being registered at all. The wrapping is
therefore a behaviour change: OCA\LaunchPad\Activity\Extension now actually
registers, which is what the requirement asked for.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.

* fix(deps): pin nextcloud/ocp to ^34.0 instead of dev-master

dev-master has advanced to the Nextcloud 35 development stubs, where
ISchemaWrapper::createTable()/getTable() return the new
OCP\DB\Schema\ITable (@SInCE 35.0.0) rather than being documented as
returning \Doctrine\DBAL\Schema\Table. That made PHPStan report 17
genuine parameter-type mismatches in the Migration table builders, which
type-hint Doctrine\DBAL\Schema\Table.

The app declares <nextcloud min-version="32" max-version="34"/>, so
analysing against NC 35 development stubs was wrong regardless. Pinning
to ^34.0 matches the constraint used by every other app in this
migration (softwarecatalog, hermiq, pipelinq) and by the merged
reference larpingapp#313.

PHPStan is now clean; no application code changes.

* ci: re-trigger Code Quality

The previous run produced zero jobs and concluded failure: it started
inside the window where ConductionNL/.github@main carried the broken
quality.yml splice from b745bf2f, repaired at 4118bca8. Nothing in this
PR touches the workflow.

* fix(tests): make the ICache test doubles match the real interface

OCP\ICache is untyped on every Nextcloud version this app supports —
'get($key)', 'set($key, $value, $ttl = 0)', 'hasKey($key)',
'remove($key)', 'clear($prefix = "")' — identical in server stable32 and
stable34. The three in-test fakes narrowed every parameter to string/int,
which PHP rejects as an incompatible declaration:

  PHP Fatal error: Declaration of Unit\Activity\FakeDebounceCache::get(
  string $key) must be compatible with OCP\ICache::get($key)

They only ever loaded because the app resolved nextcloud/ocp at
dev-master, which has advanced to the NC 35 development stubs where
ICache::get() IS typed. Pinning ocp to ^34.0 lines the stub up with the
servers this app actually declares support for, and the fakes then have to
line up too. Return types are left in place — widening a parent's absent
return type is covariant and legal.

Parameter types only; no test behaviour changes. The three affected files
run green: OK (39 tests, 80 assertions).

* fix(ci): test the whole declared Nextcloud range, not only the ceiling

appinfo/info.xml declares <nextcloud min-version="32" max-version="34"/>, but
nextcloud-test-refs was '["stable34"]' — so the declared floor and the middle
major were advertised to the App Store with no job touching either.

This is the coding-standard migration's own defect: its rollout REPLACED the
ref list instead of extending it. The programme opened by reporting that
nothing was tested on NC 34 and, in fixing that, made 32 and 33 the untested
end. Same drift, other direction.

stable34 stays first because newman, playwright and journeydoc-capture all read
fromJSON(inputs.nextcloud-test-refs)[0] as their single server.

Verified green on all three refs against nextcloud/ocp ^34 on portaliq
(run 31599055849, six PHPUnit legs: 32/33/34 x PHP 8.3/8.4).
rubenvdlinde added a commit to ConductionNL/openconnector that referenced this pull request Aug 12, 2026
)

* chore: adopt nextcloud/coding-standard, .editorconfig and NC 34

Configuration only. The reformat is the next commit on purpose, so
.git-blame-ignore-revs can name a revision containing nothing but whitespace.

- .php-cs-fixer.dist.php + conduction/coding-standard, which extends
  nextcloud/coding-standard and can only ADD to it — enforced by that package's
  invariant test, not by review.
- cs:check / cs:fix now run php-cs-fixer. They were aliases for phpcs/phpcbf,
  so the documented Nextcloud command reformatted code AWAY from Nextcloud's
  standard.
- nextcloud/coding-standard dropped as a direct dependency. It arrives
  transitively at a version conduction/coding-standard has tested against;
  declared directly it was a dead dependency with no config and no invocation.
- phpcs.xml is now a stub over the shared semantics-only ruleset, and the local
  phpcs-custom-sniffs/ copy is gone. The fleet was carrying six divergent
  versions of NamedParametersSniff.php — a custom RULE, not a setting.
- .editorconfig, verbatim from nextcloud/server. No fleet app had one, so an
  editor configured by someone's previous Nextcloud work defaulted to tabs,
  which the old ruleset then rejected.
- nextcloud/ocp -> ^34.0 and PHPUnit -> stable34. This app declared support for
  NC 34 while being analysed against 31, so a symbol REMOVED in 32/33/34 was
  invisible to the type checker. That is why the NC 34 removal of \OC::$server
  needed a hand-written PHPCS sniff.
- the stylelint glob is quoted, so stylelint expands it rather than the shell.
  Unquoted, src/**/ matches exactly one directory level and nested components
  are silently unlinted.

gate-65 (coding-standard-adoption) enforces all of the above from
ConductionNL/.github@main. This app failed it; with this commit it passes.

* style: reformat with nextcloud/coding-standard — whitespace only

Applied by php-cs-fixer with conduction/coding-standard. Tabs, same-line braces,
(int)$x, single-space concatenation, ordered imports — Nextcloud's dialect, which
this app now passes unchanged. 712 file(s), no behaviour change.

Isolated from the configuration change so .git-blame-ignore-revs can name a
revision that touches nothing but formatting. Reviewing it line by line is not a
useful activity; the previous commit is the review.

* chore: ignore the reformat commit in git blame

0a5295c touches 712 files and changes no behaviour. Without this, every line it
reflowed attributes to it and the real author is one --skip away.

GitHub honours the file automatically; locally it needs
`git config blame.ignoreRevsFile .git-blame-ignore-revs` once.

* fix: regenerate composer.lock for the new constraints

The previous commit changed composer.json without touching the lock, so
`composer install` refused with exit 4 and EVERY PHP job failed:

    Required (in require-dev) package "conduction/coding-standard" is not
    present in the lock file.
    Required (in require-dev) package "conduction/hydra-gates" is not present
    in the lock file.
    Required (in require-dev) package "nextcloud/ocp" is in the lock file as
    "v31.0.9" but that does not satisfy your constraint "^34.0".

Nothing was wrong with the reformat or the ruleset — the jobs never got as far
as running a tool. Measured on larpingapp#313 before this fix: phpcs, psalm,
phpstan and both PHPUnit legs red, all of them at `composer install`. Hydra
Gates passed in the same run, because it does not install composer
dependencies.

Now locked at conduction/coding-standard v1.0.0, conduction/hydra-gates v1.7.0,
nextcloud/ocp v34.0.2 — the last of which is the point of the exercise: this app
declares support for NC 34 and is now analysed against it.

* fix(appinfo): order info.xml elements per the App Store xs:sequence

The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. <version> and the <nextcloud>
min/max-version declaration are unchanged.

<category> was moved after <namespace> (the sequence is version, licence,
author, namespace, types, documentation, category), and the <dependencies>
children were put in php, database, lib, owncloud, nextcloud order.

Ordering alone was not sufficient here. The <dependencies><app> entry is not
an ordering problem: the App Store schema has no <app> child under
<dependencies> at any position, so it can never validate. It was also inert -
OC\App\DependencyAnalyzer::analyze() handles only architecture, php, database,
command, lib, os and the server version, and never looks at "app" - so it
enforced nothing at install time. The dependency is now recorded as a comment
in the same place.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.

* docs(ci): say why the test matrix is stable34, and that it is a narrowing

The comment above `nextcloud-test-refs` explained a choice of
`["stable32", "stable33"]` while the value directly below it read
`["stable34"]` — the config commit changed the value and left the reasoning
describing the old one. A comment that contradicts the line it sits on is
worse than no comment: the next reader trusts it.

Records the actual reason (ocp ^34 stubs and the tested server should be the
same server) and, more importantly, records what the change COSTS: info.xml
still declares `min-version="32" max-version="34"`, so the app claims three
server versions and CI now exercises one. NC32 and NC33 are unmeasured from
here, not known-good. Names both ways the gap can be closed so it is a
tracked trade rather than an accident.

No behaviour change — comment only.

* fix: a lock regenerated with --ignore-platform-reqs cannot be installed

TWO DEFECTS, both of which made a job red rather than a finding.

1. composer.lock locked packages this app's own platform cannot run.

`config.platform.php` is pinned to "8.3", and the lock carried
symfony/string v8.1.2, symfony/finder v8.1.1 and symfony/process v8.1.0 —
all three require php >=8.4.1. That is the signature of a regeneration run
with a blanket `--ignore-platform-reqs`: the flag suppresses the PHP version
constraint as well as the extension ones, so composer resolves against a
platform that does not exist here and writes the result down as if it did.

`composer validate` passes on such a lock — the content-hash still matches
composer.json — so the defect is invisible to the check most likely to be
run, and only surfaces at install time. In CI it took out NINE jobs, all
with the same line and none of them naming a package this app requires
directly:

  Your lock file does not contain a compatible set of packages.

  PHP Quality (lint / phpcs / phpmd / phpstan / psalm / phpmetrics)
  Security (composer), License (composer)
  PHPUnit (8.3 and 8.4), Integration Tests (Newman), E2E (Playwright)

and it cascaded one gate further: gate-33 axe-core reported SKIPPED
(structural) because the Playwright job that produces its report never ran.

Re-resolved with the three packages named explicitly and `--ignore-platform-
req=ext-*` — extension constraints only, so the PHP version stays enforced.
They downgrade to the 7.4 line, which is where the rest of the symfony
packages here already are:

  symfony/finder   v8.1.1 -> v7.4.14
  symfony/process  v8.1.0 -> v7.4.13
  symfony/string   v8.1.2 -> v7.4.15

`nextcloud/ocp` is unaffected and still resolves to v34.0.2, which is the
point of this PR. `composer install --dry-run` under the pinned 8.3 platform
now completes, and a sweep of every locked package's `require.php` finds
zero above 8.3 — the same sweep over the previous lock finds exactly those
three, so it is a measurement and not a hopeful assertion.

2. appinfo/info.xml did not validate against the App Store schema.

`info.xml lint` landed in the shared workflow today
(ConductionNL/.github#383) and this is the first run that had it. The App
Store's info.xsd declares repair-steps as an xs:sequence — pre-migration,
post-migration, live-migration, install, uninstall — so `<install>` must
come AFTER `<post-migration>`, which is the opposite of the order they run
in and the opposite of how #1224 wrote them:

  appinfo/info.xml:115: element post-migration: Schemas validity error :
  Element 'post-migration': This element is not expected.
  Expected is ( uninstall ).

Swapped, with the constraint written down next to it so the intuitive order
does not come back. Nothing about which hooks run, or when, changes.

⚠️ THE BASE BRANCH FAILS THIS CHECK TOO, and earlier: `origin/development`'s
info.xml is rejected at line 28 with `Element 'category': This element is
not expected. Expected is one of ( licence, author )`. This branch already
fixed that half in d960a2f. Verified by running xmllint against the real
appstore info.xsd over both files — head validates, base does not.

* fix: clear the two quality legs the new ruleset and the reformat turned red

phpstan — ONE error, and the reformat is what created it.

  Deprecated in PHP 8.1: Required parameter $flowToken follows optional
  parameter $synchronization.  lib/Service/SynchronizationService.php

`synchronizeContract()` reads the same on both branches, but not to PHP. On
`development` the parameter is written `array $synchronization=null`, which
PHP treats as IMPLICITLY NULLABLE and therefore exempt from the 8.1
"required after optional" deprecation. php-cs-fixer's
`nullable_type_declaration_for_default_null_value` rewrites that to
`?array $synchronization = null` — now genuinely optional, and the required
`FlowToken &$flowToken` behind it becomes a deprecation. The signature did
not change meaning; it changed CATEGORY.

Fixed by moving `$flowToken` ahead of the optional parameters rather than by
undoing the rule, because the rule is the standard this PR adopts and
`cs:fix` would rewrite it back. All four call sites pass every argument by
NAME, and the test suite only ever mocks the method with `willReturn` — no
`->with()` positional assertion anywhere — so the position is free to move.
A scan of every signature in lib/ for a required parameter after an optional
one now returns nothing; the same scan over the previous commit returns
exactly this one, so it is a measurement.

phpcs — 21 errors, all from the ruleset switch, all now zero.

phpcs.xml moved to the shared semantics-only ruleset in this PR, so the tree
is being read by a different set of sniffs than the one `development` passes
under. It is a different question, not a regression, and the answer was 21:

  1  missing docblock  #1233 inserted `dispatchesThroughBroker()`'s docblock
                       BETWEEN `resolveBrokeredDispatch()`'s docblock and its
                       signature, orphaning the first and leaving the second
                       undocumented. Moved the docblock back onto its own
                       function; no code moved.
  11 param alignment   one BankfeedSyncService docblock indented its
                       continuation lines to a column no @PARAM sits at.
  9  line length       over 150 characters.

⚠️ THE OBVIOUS FIX FOR THE LONG LINES DOES NOT HOLD, and this is the part
worth knowing before the next app in the migration hits it. Five of them are
`@return <very wide array shape>  <description>`, and php-cs-fixer's
`phpdoc_align` re-indents the continuation under the type column. Manually
re-wrapping them at a sane indent makes `cs:fix` put them straight back —
the two tools genuinely disagree, and phpcs is the one that fails the build.
Moved the prose OUT of the tag and into the docblock body instead, which
leaves no continuation line for either tool to have an opinion about. Two
more were over-long signatures, wrapped one parameter per line.

Verified in the same container CI uses (php:8.3-cli, this repo's own vendor):

  php-cs-fixer  Fixed 0 of 714 files          (stable — cs:fix is a fixpoint)
  phpcs         0 ERRORS AND 277 WARNINGS     (the job passes on warnings)
  phpstan       [OK] No errors

* fix(ci): test the whole declared Nextcloud range, not only the ceiling

appinfo/info.xml declares <nextcloud min-version="32" max-version="34"/>, but
nextcloud-test-refs was '["stable34"]' — so the declared floor and the middle
major were advertised to the App Store with no job touching either.

This is the coding-standard migration's own defect: its rollout REPLACED the
ref list instead of extending it. The programme opened by reporting that
nothing was tested on NC 34 and, in fixing that, made 32 and 33 the untested
end. Same drift, other direction.

stable34 stays first because newman, playwright and journeydoc-capture all read
fromJSON(inputs.nextcloud-test-refs)[0] as their single server.

Verified green on all three refs against nextcloud/ocp ^34 on portaliq
(run 31599055849, six PHPUnit legs: 32/33/34 x PHP 8.3/8.4).
rubenvdlinde added a commit to ConductionNL/scholiq that referenced this pull request Aug 12, 2026
* chore: adopt nextcloud/coding-standard, .editorconfig and NC 34

Configuration only. The reformat is the next commit on purpose, so
.git-blame-ignore-revs can name a revision containing nothing but whitespace.

- .php-cs-fixer.dist.php + conduction/coding-standard, which extends
  nextcloud/coding-standard and can only ADD to it — enforced by that package's
  invariant test, not by review.
- cs:check / cs:fix now run php-cs-fixer. They were aliases for phpcs/phpcbf,
  so the documented Nextcloud command reformatted code AWAY from Nextcloud's
  standard.
- nextcloud/coding-standard dropped as a direct dependency. It arrives
  transitively at a version conduction/coding-standard has tested against;
  declared directly it was a dead dependency with no config and no invocation.
- phpcs.xml is now a stub over the shared semantics-only ruleset, and the local
  phpcs-custom-sniffs/ copy is gone. The fleet was carrying six divergent
  versions of NamedParametersSniff.php — a custom RULE, not a setting.
- .editorconfig, verbatim from nextcloud/server. No fleet app had one, so an
  editor configured by someone's previous Nextcloud work defaulted to tabs,
  which the old ruleset then rejected.
- nextcloud/ocp -> ^34.0 and PHPUnit -> stable34. This app declared support for
  NC 34 while being analysed against 31, so a symbol REMOVED in 32/33/34 was
  invisible to the type checker. That is why the NC 34 removal of \OC::$server
  needed a hand-written PHPCS sniff.
- the stylelint glob is quoted, so stylelint expands it rather than the shell.
  Unquoted, src/**/ matches exactly one directory level and nested components
  are silently unlinted.

gate-65 (coding-standard-adoption) enforces all of the above from
ConductionNL/.github@main. This app failed it; with this commit it passes.

* style: reformat with nextcloud/coding-standard — whitespace only

Applied by php-cs-fixer with conduction/coding-standard. Tabs, same-line braces,
(int)$x, single-space concatenation, ordered imports — Nextcloud's dialect, which
this app now passes unchanged. 386 file(s), no behaviour change.

Isolated from the configuration change so .git-blame-ignore-revs can name a
revision that touches nothing but formatting. Reviewing it line by line is not a
useful activity; the previous commit is the review.

* chore: ignore the reformat commit in git blame

2fd6f31 touches 386 files and changes no behaviour. Without this, every line it
reflowed attributes to it and the real author is one --skip away.

GitHub honours the file automatically; locally it needs
`git config blame.ignoreRevsFile .git-blame-ignore-revs` once.

* fix: regenerate composer.lock for the new constraints

The previous commit changed composer.json without touching the lock, so
`composer install` refused with exit 4 and EVERY PHP job failed:

    Required (in require-dev) package "conduction/coding-standard" is not
    present in the lock file.
    Required (in require-dev) package "conduction/hydra-gates" is not present
    in the lock file.
    Required (in require-dev) package "nextcloud/ocp" is in the lock file as
    "v31.0.9" but that does not satisfy your constraint "^34.0".

Nothing was wrong with the reformat or the ruleset — the jobs never got as far
as running a tool. Measured on larpingapp#313 before this fix: phpcs, psalm,
phpstan and both PHPUnit legs red, all of them at `composer install`. Hydra
Gates passed in the same run, because it does not install composer
dependencies.

Now locked at conduction/coding-standard v1.0.0, conduction/hydra-gates v1.7.0,
nextcloud/ocp v34.0.2 — the last of which is the point of the exercise: this app
declares support for NC 34 and is now analysed against it.

* fix(appinfo): order info.xml elements per the App Store xs:sequence

The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. <version> and the <nextcloud>
min/max-version declaration are unchanged.

Top-level blocks are now background-jobs, repair-steps, settings, navigations;
<php> precedes <nextcloud>; and inside <repair-steps>, <post-migration>
precedes <install> (the schema's order is pre-migration, post-migration,
live-migration, install, uninstall - it does not reflect execution order,
which Nextcloud selects by event).

Ordering alone was not sufficient here. The <dependencies><app> entry is not
an ordering problem: the App Store schema has no <app> child under
<dependencies> at any position, so it can never validate. It was also inert -
OC\App\DependencyAnalyzer::analyze() handles only architecture, php, database,
command, lib, os and the server version, and never looks at "app" - so it
enforced nothing at install time. The dependency is now recorded as a comment
in the same place.
Both entries (openregister and openconnector) were converted; the openregister
version floor stays machine-readable as the constraint in
lib/Settings/scholiq_register.json.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.

* style(stylelint): clear the lint debt the widened glob newly exposes

The config commit quoted the previously-unquoted stylelint glob. Unquoted,
/bin/sh (dash) expanded 'src/**/*.vue' as 'src/*/*.vue' — one directory deep —
so every component nested two levels under src/ was silently never linted.
Quoting it hands the pattern to stylelint, whose own glob engine recurses.

These violations are pre-existing debt in newly-covered files, not regressions:
every offending file sits at src/<a>/<b>/<file>.vue and was matched zero times
by the old expansion. Fixed with stylelint --fix (rule-empty-line-before) plus
one hand-removed verbatim duplicate selector where --fix could not.

No hardcoded colours introduced; the glob is left widened.

* ci: re-run against the repaired shared quality.yml

The previous run hit a startup failure: quality.yml@main carried a duplicate
'name:' key and a job with no steps, spliced in by .github#385. Repaired by
.github#389. A startup-failed run cannot be re-run, so this empty commit
requests a fresh evaluation.

* fix: drop an accidentally committed node_modules symlink

Local tooling symlink picked up by 'git add -A'. It escaped .gitignore because
the rule is 'node_modules/' with a trailing slash, which matches a DIRECTORY —
a symlink of the same name is a blob (mode 120000), not a directory, so the
rule never applied. It pointed at an absolute path outside the repo.

* fix(ci): test the whole declared Nextcloud range, not only the ceiling

appinfo/info.xml declares <nextcloud min-version="32" max-version="34"/>, but
nextcloud-test-refs was '["stable34"]' — so the declared floor and the middle
major were advertised to the App Store with no job touching either.

This is the coding-standard migration's own defect: its rollout REPLACED the
ref list instead of extending it. The programme opened by reporting that
nothing was tested on NC 34 and, in fixing that, made 32 and 33 the untested
end. Same drift, other direction.

stable34 stays first because newman, playwright and journeydoc-capture all read
fromJSON(inputs.nextcloud-test-refs)[0] as their single server.

Verified green on all three refs against nextcloud/ocp ^34 on portaliq
(run 31599055849, six PHPUnit legs: 32/33/34 x PHP 8.3/8.4).
rubenvdlinde added a commit to ConductionNL/openbuild that referenced this pull request Aug 12, 2026
* chore: adopt nextcloud/coding-standard, .editorconfig and NC 34

Configuration only. The reformat is the next commit on purpose, so
.git-blame-ignore-revs can name a revision containing nothing but whitespace.

- .php-cs-fixer.dist.php + conduction/coding-standard, which extends
  nextcloud/coding-standard and can only ADD to it — enforced by that package's
  invariant test, not by review.
- cs:check / cs:fix now run php-cs-fixer. They were aliases for phpcs/phpcbf,
  so the documented Nextcloud command reformatted code AWAY from Nextcloud's
  standard.
- nextcloud/coding-standard dropped as a direct dependency. It arrives
  transitively at a version conduction/coding-standard has tested against;
  declared directly it was a dead dependency with no config and no invocation.
- phpcs.xml is now a stub over the shared semantics-only ruleset, and the local
  phpcs-custom-sniffs/ copy is gone. The fleet was carrying six divergent
  versions of NamedParametersSniff.php — a custom RULE, not a setting.
- .editorconfig, verbatim from nextcloud/server. No fleet app had one, so an
  editor configured by someone's previous Nextcloud work defaulted to tabs,
  which the old ruleset then rejected.
- nextcloud/ocp -> ^34.0 and PHPUnit -> stable34. This app declared support for
  NC 34 while being analysed against 31, so a symbol REMOVED in 32/33/34 was
  invisible to the type checker. That is why the NC 34 removal of \OC::$server
  needed a hand-written PHPCS sniff.
- the stylelint glob is quoted, so stylelint expands it rather than the shell.
  Unquoted, src/**/ matches exactly one directory level and nested components
  are silently unlinted.

gate-65 (coding-standard-adoption) enforces all of the above from
ConductionNL/.github@main. This app failed it; with this commit it passes.

* style: reformat with nextcloud/coding-standard — whitespace only

Applied by php-cs-fixer with conduction/coding-standard. Tabs, same-line braces,
(int)$x, single-space concatenation, ordered imports — Nextcloud's dialect, which
this app now passes unchanged. 236 file(s), no behaviour change.

Isolated from the configuration change so .git-blame-ignore-revs can name a
revision that touches nothing but formatting. Reviewing it line by line is not a
useful activity; the previous commit is the review.

* chore: ignore the reformat commit in git blame

e78eb70 touches 236 files and changes no behaviour. Without this, every line it
reflowed attributes to it and the real author is one --skip away.

GitHub honours the file automatically; locally it needs
`git config blame.ignoreRevsFile .git-blame-ignore-revs` once.

* fix: regenerate composer.lock for the new constraints

The previous commit changed composer.json without touching the lock, so
`composer install` refused with exit 4 and EVERY PHP job failed:

    Required (in require-dev) package "conduction/coding-standard" is not
    present in the lock file.
    Required (in require-dev) package "conduction/hydra-gates" is not present
    in the lock file.
    Required (in require-dev) package "nextcloud/ocp" is in the lock file as
    "v31.0.9" but that does not satisfy your constraint "^34.0".

Nothing was wrong with the reformat or the ruleset — the jobs never got as far
as running a tool. Measured on larpingapp#313 before this fix: phpcs, psalm,
phpstan and both PHPUnit legs red, all of them at `composer install`. Hydra
Gates passed in the same run, because it does not install composer
dependencies.

Now locked at conduction/coding-standard v1.0.0, conduction/hydra-gates v1.7.0,
nextcloud/ocp v34.0.2 — the last of which is the point of the exercise: this app
declares support for NC 34 and is now analysed against it.

* fix(appinfo): order info.xml elements per the App Store xs:sequence

The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. <version> and the <nextcloud>
min/max-version declaration are unchanged.

Top-level blocks are now background-jobs, repair-steps, commands, settings,
navigations; <php> precedes <nextcloud>; and inside <repair-steps>,
<post-migration> precedes <install> (the schema's order is pre-migration,
post-migration, live-migration, install, uninstall - it does not reflect
execution order, which Nextcloud selects by event).

Ordering alone was not sufficient here. The <dependencies><app> entry is not
an ordering problem: the App Store schema has no <app> child under
<dependencies> at any position, so it can never validate. It was also inert -
OC\App\DependencyAnalyzer::analyze() handles only architecture, php, database,
command, lib, os and the server version, and never looks at "app" - so it
enforced nothing at install time. The dependency is now recorded as a comment
in the same place.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.

* fix(ci): test the whole declared Nextcloud range, not only the ceiling

appinfo/info.xml declares <nextcloud min-version="32" max-version="34"/>, but
nextcloud-test-refs was '["stable34"]' — so the declared floor and the middle
major were advertised to the App Store with no job touching either.

This is the coding-standard migration's own defect: its rollout REPLACED the
ref list instead of extending it. The programme opened by reporting that
nothing was tested on NC 34 and, in fixing that, made 32 and 33 the untested
end. Same drift, other direction.

stable34 stays first because newman, playwright and journeydoc-capture all read
fromJSON(inputs.nextcloud-test-refs)[0] as their single server.

Verified green on all three refs against nextcloud/ocp ^34 on portaliq
(run 31599055849, six PHPUnit legs: 32/33/34 x PHP 8.3/8.4).

* fix(deps): require symfony/http-foundation for the NC 34 DownloadResponse

nextcloud/ocp v34's OCP\AppFramework\Http\DownloadResponse references
Symfony\Component\HttpFoundation\HeaderUtils, which was not in the
dependency tree. Three ExportsControllerTest download cases errored with
'Class "Symfony\Component\HttpFoundation\HeaderUtils" not found'.

Add symfony/http-foundation ^6.4 to require-dev so the class resolves
under static analysis and PHPUnit.

* fix(stubs): type GenericAdminSettings::$initialState as the AppFramework service

The stub declared OCP\IInitialState, but OpenRegister's real
AppHost\Settings\GenericAdminSettings takes
OCP\AppFramework\Services\IInitialState. Application::register()
correctly passes the AppFramework service, so PHPStan reported a false
type mismatch at AppInfo/Application.php:322 against the inaccurate stub.

Align the stub with the class it stands in for.

* style(vue): add the blank lines stylelint requires before adjacent rules

The quoted stylelint glob widened coverage to .vue files that had never
been linted, surfacing 69 pre-existing rule-empty-line-before violations
across ten components. Whitespace only; no selector, declaration or
template changes.

Also quote the glob in the stylelint-fix script so --fix covers the same
file set the stylelint check does.
rubenvdlinde added a commit to ConductionNL/openregister that referenced this pull request Aug 12, 2026
)

* chore: adopt nextcloud/coding-standard, .editorconfig and NC 34

Configuration only. The reformat is the next commit on purpose, so
.git-blame-ignore-revs can name a revision containing nothing but whitespace.

- .php-cs-fixer.dist.php + conduction/coding-standard, which extends
  nextcloud/coding-standard and can only ADD to it — enforced by that package's
  invariant test, not by review.
- cs:check / cs:fix now run php-cs-fixer. They were aliases for phpcs/phpcbf,
  so the documented Nextcloud command reformatted code AWAY from Nextcloud's
  standard.
- nextcloud/coding-standard dropped as a direct dependency. It arrives
  transitively at a version conduction/coding-standard has tested against;
  declared directly it was a dead dependency with no config and no invocation.
- phpcs.xml is now a stub over the shared semantics-only ruleset, and the local
  phpcs-custom-sniffs/ copy is gone. The fleet was carrying six divergent
  versions of NamedParametersSniff.php — a custom RULE, not a setting.
- .editorconfig, verbatim from nextcloud/server. No fleet app had one, so an
  editor configured by someone's previous Nextcloud work defaulted to tabs,
  which the old ruleset then rejected.
- nextcloud/ocp -> ^34.0 and PHPUnit -> stable34. This app declared support for
  NC 34 while being analysed against 31, so a symbol REMOVED in 32/33/34 was
  invisible to the type checker. That is why the NC 34 removal of \OC::$server
  needed a hand-written PHPCS sniff.
- the stylelint glob is quoted, so stylelint expands it rather than the shell.
  Unquoted, src/**/ matches exactly one directory level and nested components
  are silently unlinted.

gate-65 (coding-standard-adoption) enforces all of the above from
ConductionNL/.github@main. This app failed it; with this commit it passes.

* style: reformat with nextcloud/coding-standard — whitespace only

Applied by php-cs-fixer with conduction/coding-standard. Tabs, same-line braces,
(int)$x, single-space concatenation, ordered imports — Nextcloud's dialect, which
this app now passes unchanged. 2574 file(s), no behaviour change.

Isolated from the configuration change so .git-blame-ignore-revs can name a
revision that touches nothing but formatting. Reviewing it line by line is not a
useful activity; the previous commit is the review.

* chore: ignore the reformat commit in git blame

a34f528 touches 2574 files and changes no behaviour. Without this, every line it
reflowed attributes to it and the real author is one --skip away.

GitHub honours the file automatically; locally it needs
`git config blame.ignoreRevsFile .git-blame-ignore-revs` once.

* fix: regenerate composer.lock for the new constraints

The previous commit changed composer.json without touching the lock, so
`composer install` refused with exit 4 and EVERY PHP job failed:

    Required (in require-dev) package "conduction/coding-standard" is not
    present in the lock file.
    Required (in require-dev) package "conduction/hydra-gates" is not present
    in the lock file.
    Required (in require-dev) package "nextcloud/ocp" is in the lock file as
    "v31.0.9" but that does not satisfy your constraint "^34.0".

Nothing was wrong with the reformat or the ruleset — the jobs never got as far
as running a tool. Measured on larpingapp#313 before this fix: phpcs, psalm,
phpstan and both PHPUnit legs red, all of them at `composer install`. Hydra
Gates passed in the same run, because it does not install composer
dependencies.

Now locked at conduction/coding-standard v1.0.0, conduction/hydra-gates v1.7.0,
nextcloud/ocp v34.0.2 — the last of which is the point of the exercise: this app
declares support for NC 34 and is now analysed against it.

* fix(appinfo): order info.xml elements per the App Store xs:sequence

The App Store's info.xsd declares <info> and its children as xs:sequence, so
element ORDER is significant. This file was rejected by
`xmllint --noout --schema info.xsd appinfo/info.xml`. Nextcloud's
lint-info-xml workflow validates against exactly that schema, and
ConductionNL/.github#383 adds the same check to the shared pipeline.

Elements were moved into the schema's order. <version> and the <nextcloud>
min/max-version declaration are unchanged.

<documentation> moved ahead of <category>; top-level blocks are now
background-jobs, repair-steps, commands, settings, activity, navigations,
contactsmenu; and <activity>'s children are in settings, filters, providers
order.

One content fix was unavoidable. <notification><service> is not an ordering
problem - the App Store schema has no <notification> element at any position,
and Nextcloud's InfoParser never read one, so the declaration registered
nothing. The notifier is registered in PHP, via
registerNotifierService(AnnotationNotifier::class) in lib/AppInfo/Application.php,
which is unaffected. Replaced by a comment pointing at that registration.

Verified: `xmllint --noout --schema info.xsd appinfo/info.xml` reports
"validates" (libxml2 2.12.10). The pre-change file failed the same command.

* fix: make phpcs, psalm and phpstan pass under the new ruleset and NC 34

Three separate causes, all of them this branch's doing, none of them a
defect in the code being reformatted.

phpcs (26 errors -> 0)
  The stub ruleset enforces a 150-character line limit. php-cs-fixer's
  phpdoc_align aligns a wrapped `@return` continuation to the column
  after the type on the FIRST line, so a long array shape pushes its own
  description past 150 — the formatter mechanically creates the
  violation the linter then reports. Measured: `git show
  origin/development:<file>` has ZERO over-long lines in every file
  flagged here. The fix is to stop hanging prose off a long `@return`
  and put it in the description, where it reads better anyway.

  AggregationRunner keeps its wrapped union and gains an `AggValues`
  alias instead. Joining that union onto one line makes PHPStan resolve
  it properly for the first time, which surfaces unrelated pre-existing
  type debt two thousand lines away — not this commit's business.

  SettingsService::convertToBytes() loses its switch fall-through.
  php-cs-fixer's no_break_comment requires the marker to read exactly
  `no break`; PHPCS's Squiz.Commenting.InlineComment.NotCapital requires
  a capital. No spelling satisfies both, so the fall-through goes and
  each case states its own multiplier. Same arithmetic.

psalm (5 errors -> 0), baseline regenerated
  Two of the three drifted entries drifted for a reason worth naming:
  psalm baselines match on a CODE SNIPPET, and the reformat moved the
  opening brace onto the declaration line, so
  `public function __construct(Entity $entity, string $systemSlug)`
  stopped matching `... $systemSlug) {`. Any app in this fleet with a
  snippet-matched psalm baseline will drift the same way on reformat.
  The third, allowEvalScript, is an NC 34 removal.

phpstan (21 errors -> 0), baselined in phpstan-baseline-nc34-ocp.neon
  All 21 come from analysing against ocp 34 instead of 31. Pre-existing
  code; the analyser just got the API the app claims to support. Four
  are real NC 34 removals that will fatal at runtime. Baselined rather
  than fixed because porting to NC 34 is not a reformat, and because
  several of the rest are constant-false comparisons where the
  always-false predicate may be the only thing suppressing a wrong code
  path. Every entry is diagnosed in openregister#2450.

phpmd was already clean on the merged tree; no change needed.

* fix(ci): test the whole declared Nextcloud range, not only the ceiling

appinfo/info.xml declares <nextcloud min-version="32" max-version="34"/>, but
nextcloud-test-refs was '["stable34"]' — so the declared floor and the middle
major were advertised to the App Store with no job touching either.

This is the coding-standard migration's own defect: its rollout REPLACED the
ref list instead of extending it. The programme opened by reporting that
nothing was tested on NC 34 and, in fixing that, made 32 and 33 the untested
end. Same drift, other direction.

stable34 stays first because newman, playwright and journeydoc-capture all read
fromJSON(inputs.nextcloud-test-refs)[0] as their single server.

Verified green on all three refs against nextcloud/ocp ^34 on portaliq
(run 31599055849, six PHPUnit legs: 32/33/34 x PHP 8.3/8.4).

* fix(phpmd): import RegistryStepDispatcher instead of instantiating it FQN

phpmd's MissingImport fired on the `new` in the FlowStepDispatcher factory.
The rule only looks at instantiation, so the two `::class` references beside
it are untouched and stay fully qualified.

Pre-existing rather than new: the same violation is red on development
(run 31604485669, job 94139819972, Application.php:571) — it arrived with
the fan-out commit abb9556 and this branch merely inherited it in the
merge. Fixed here rather than left behind because it lands in a file this
merge already touches, and this repo fixes quality issues it encounters.

Verified: phpmd exits 0 on lib/AppInfo with the repo's own ruleset and
baseline, and a control that restores the FQN reproduces the CI finding
(MissingImport, exit 2). php-cs-fixer reports 0 of 61 files needing change,
so the added import sits where the coding standard wants it.

---------

Co-authored-by: Ruben van der Linde <juan.claude@conduction.nl>
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