Skip to content

Release: merge development into beta - #90

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

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

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

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

Merging this PR will trigger the beta release workflow.

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

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report

Repository ConductionNL/larpingapp
Commit e341203
Branch 90/merge
Event pull_request
Generated 2026-03-19 18:55 UTC
Workflow Run https://github.com/ConductionNL/larpingapp/actions/runs/23311646375

Summary

Group Result
PHP Quality PASS
Vue Quality PASS
Security PASS
License PASS
PHPUnit SKIP
Newman SKIP

PHP Quality

Tool Result
lint PASS
phpcs PASS
phpmd PASS
psalm PASS
phpstan PASS
phpmetrics PASS

Vue Quality

Tool Result
eslint PASS
stylelint PASS

Security

Ecosystem Result
composer PASS
npm PASS

License Compliance

Ecosystem Result
composer PASS
npm PASS

composer dependencies (94 total)

Metric Count
Approved (allowlist) 94
Approved (override) 0
Denied 0

npm dependencies (248 total)

Metric Count
Approved (allowlist) 247
Approved (override) 1
Denied 0

PHPUnit Tests

PHPUnit tests were not enabled for this run.

Integration Tests (Newman)

Newman integration tests were not enabled for this run.


Generated automatically by the Quality workflow.

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report

Repository ConductionNL/larpingapp
Commit 473e6d9
Branch 90/merge
Event pull_request
Generated 2026-03-19 18:58 UTC
Workflow Run https://github.com/ConductionNL/larpingapp/actions/runs/23311789570

Summary

Group Result
PHP Quality PASS
Vue Quality PASS
Security PASS
License PASS
PHPUnit SKIP
Newman SKIP

PHP Quality

Tool Result
lint PASS
phpcs PASS
phpmd PASS
psalm PASS
phpstan PASS
phpmetrics PASS

Vue Quality

Tool Result
eslint PASS
stylelint PASS

Security

Ecosystem Result
composer PASS
npm PASS

License Compliance

Ecosystem Result
composer PASS
npm PASS

composer dependencies (94 total)

Metric Count
Approved (allowlist) 94
Approved (override) 0
Denied 0

npm dependencies (248 total)

Metric Count
Approved (allowlist) 247
Approved (override) 1
Denied 0

PHPUnit Tests

PHPUnit tests were not enabled for this run.

Integration Tests (Newman)

Newman integration tests were not enabled for this run.


Generated automatically by the Quality workflow.

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report

Repository ConductionNL/larpingapp
Commit e047e5b
Branch 90/merge
Event pull_request
Generated 2026-03-19 19:05 UTC
Workflow Run https://github.com/ConductionNL/larpingapp/actions/runs/23312031182

Summary

Group Result
PHP Quality PASS
Vue Quality PASS
Security PASS
License PASS
PHPUnit SKIP
Newman SKIP

PHP Quality

Tool Result
lint PASS
phpcs PASS
phpmd PASS
psalm PASS
phpstan PASS
phpmetrics PASS

Vue Quality

Tool Result
eslint PASS
stylelint PASS

Security

Ecosystem Result
composer PASS
npm PASS

License Compliance

Ecosystem Result
composer PASS
npm PASS

composer dependencies (94 total)

Metric Count
Approved (allowlist) 94
Approved (override) 0
Denied 0

npm dependencies (248 total)

Metric Count
Approved (allowlist) 247
Approved (override) 1
Denied 0

PHPUnit Tests

PHPUnit tests were not enabled for this run.

Integration Tests (Newman)

Newman integration tests were not enabled for this run.


Generated automatically by the Quality workflow.

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report

Repository ConductionNL/larpingapp
Commit f41b40e
Branch 90/merge
Event pull_request
Generated 2026-03-19 19:09 UTC
Workflow Run https://github.com/ConductionNL/larpingapp/actions/runs/23312246198

Summary

Group Result
PHP Quality PASS
Vue Quality PASS
Security PASS
License PASS
PHPUnit SKIP
Newman SKIP

PHP Quality

Tool Result
lint PASS
phpcs PASS
phpmd PASS
psalm PASS
phpstan PASS
phpmetrics PASS

Vue Quality

Tool Result
eslint PASS
stylelint PASS

Security

Ecosystem Result
composer PASS
npm PASS

License Compliance

Ecosystem Result
composer PASS
npm PASS

composer dependencies (94 total)

Metric Count
Approved (allowlist) 94
Approved (override) 0
Denied 0

npm dependencies (248 total)

Metric Count
Approved (allowlist) 247
Approved (override) 1
Denied 0

PHPUnit Tests

PHPUnit tests were not enabled for this run.

Integration Tests (Newman)

Newman integration tests were not enabled for this run.


Generated automatically by the Quality workflow.

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report

Repository ConductionNL/larpingapp
Commit 0644c0a
Branch 90/merge
Event pull_request
Generated 2026-03-19 19:12 UTC
Workflow Run https://github.com/ConductionNL/larpingapp/actions/runs/23312369818

Summary

Group Result
PHP Quality PASS
Vue Quality PASS
Security PASS
License PASS
PHPUnit SKIP
Newman SKIP

PHP Quality

Tool Result
lint PASS
phpcs PASS
phpmd PASS
psalm PASS
phpstan PASS
phpmetrics PASS

Vue Quality

Tool Result
eslint PASS
stylelint PASS

Security

Ecosystem Result
composer PASS
npm PASS

License Compliance

Ecosystem Result
composer PASS
npm PASS

composer dependencies (94 total)

Metric Count
Approved (allowlist) 94
Approved (override) 0
Denied 0

npm dependencies (248 total)

Metric Count
Approved (allowlist) 247
Approved (override) 1
Denied 0

PHPUnit Tests

PHPUnit tests were not enabled for this run.

Integration Tests (Newman)

Newman integration tests were not enabled for this run.


Generated automatically by the Quality workflow.

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report

Repository ConductionNL/larpingapp
Commit 68e5a77
Branch 90/merge
Event pull_request
Generated 2026-03-19 21:37 UTC
Workflow Run https://github.com/ConductionNL/larpingapp/actions/runs/23318051893

Summary

Group Result
PHP Quality PASS
Vue Quality PASS
Security PASS
License PASS
PHPUnit SKIP
Newman SKIP

PHP Quality

Tool Result
lint PASS
phpcs PASS
phpmd PASS
psalm PASS
phpstan PASS
phpmetrics PASS

Vue Quality

Tool Result
eslint PASS
stylelint PASS

Security

Ecosystem Result
composer PASS
npm PASS

License Compliance

Ecosystem Result
composer PASS
npm PASS

composer dependencies (94 total)

Metric Count
Approved (allowlist) 94
Approved (override) 0
Denied 0

npm dependencies (248 total)

Metric Count
Approved (allowlist) 247
Approved (override) 1
Denied 0

PHPUnit Tests

PHPUnit tests were not enabled for this run.

Integration Tests (Newman)

Newman integration tests were not enabled for this run.


Generated automatically by the Quality workflow.

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report

Repository ConductionNL/larpingapp
Commit 2aef093
Branch 90/merge
Event pull_request
Generated 2026-03-23 21:38 UTC
Workflow Run https://github.com/ConductionNL/larpingapp/actions/runs/23461376504

Summary

Group Result
PHP Quality PASS
Vue Quality FAIL
Security PASS
License PASS
PHPUnit SKIP
Newman SKIP

PHP Quality

Tool Result
lint PASS
phpcs PASS
phpmd PASS
psalm PASS
phpstan PASS
phpmetrics PASS

Vue Quality

Tool Result
eslint FAIL
stylelint PASS

Security

Ecosystem Result
composer PASS
npm PASS

License Compliance

Ecosystem Result
composer PASS
npm PASS

composer dependencies (94 total)

Metric Count
Approved (allowlist) 94
Approved (override) 0
Denied 0

npm dependencies (248 total)

Metric Count
Approved (allowlist) 247
Approved (override) 1
Denied 0

PHPUnit Tests

PHPUnit tests were not enabled for this run.

Integration Tests (Newman)

Newman integration tests were not enabled for this run.


Generated automatically by the Quality workflow.

Download the full PDF report from the workflow artifacts.

@github-actions

github-actions Bot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report

Repository ConductionNL/larpingapp
Commit 3fded4f
Branch 90/merge
Event pull_request
Generated 2026-04-09 09:47 UTC
Workflow Run https://github.com/ConductionNL/larpingapp/actions/runs/24183657526

Summary

Group Result
PHP Quality PASS
Vue Quality FAIL
Security PASS
License PASS
PHPUnit SKIP
Newman SKIP
Playwright SKIP

PHP Quality

Tool Result
lint PASS
phpcs PASS
phpmd PASS
psalm PASS
phpstan PASS
phpmetrics PASS

Vue Quality

Tool Result
eslint FAIL
stylelint PASS

Security

Ecosystem Result
composer PASS
npm PASS

License Compliance

Ecosystem Result
composer PASS
npm PASS

composer dependencies (94 total)

Metric Count
Approved (allowlist) 94
Approved (override) 0
Denied 0

npm dependencies (248 total)

Metric Count
Approved (allowlist) 247
Approved (override) 1
Denied 0

PHPUnit Tests

PHPUnit tests were not enabled for this run.

Integration Tests (Newman)

Newman integration tests were not enabled for this run.

E2E Tests (Playwright)

Playwright E2E tests were not enabled for this run.


Generated automatically by the Quality workflow.

Download the full PDF report from the workflow artifacts.

@github-actions

github-actions Bot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report

Repository ConductionNL/larpingapp
Commit f2c3b5d
Branch 90/merge
Event pull_request
Generated 2026-04-09 10:24 UTC
Workflow Run https://github.com/ConductionNL/larpingapp/actions/runs/24184200585

Summary

Group Result
PHP Quality PASS
Vue Quality FAIL
Security PASS
License PASS
PHPUnit SKIP
Newman SKIP
Playwright SKIP

PHP Quality

Tool Result
lint PASS
phpcs PASS
phpmd PASS
psalm PASS
phpstan PASS
phpmetrics PASS

Vue Quality

Tool Result
eslint FAIL
stylelint PASS

Security

Ecosystem Result
composer PASS
npm PASS

License Compliance

Ecosystem Result
composer PASS
npm PASS

composer dependencies (94 total)

Metric Count
Approved (allowlist) 94
Approved (override) 0
Denied 0

npm dependencies (248 total)

Metric Count
Approved (allowlist) 247
Approved (override) 1
Denied 0

PHPUnit Tests

PHPUnit tests were not enabled for this run.

Integration Tests (Newman)

Newman integration tests were not enabled for this run.

E2E Tests (Playwright)

Playwright E2E tests were not enabled for this run.


Generated automatically by the Quality workflow.

Download the full PDF report from the workflow artifacts.

@github-actions

github-actions Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ b546553

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm ✅ 248/248
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-05-03 15:17 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

github-actions Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ c1a4891

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-05-03 17:46 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

github-actions Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ d8ed25b

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-05-03 18:04 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

github-actions Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ 4088ad6

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-05-07 20:52 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

github-actions Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ 12b7df9

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-05-07 21:24 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ c5aa97a

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-05-12 21:23 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ 446d4ca

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-05-12 21:55 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ 7a3fde5

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-05-12 22:33 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ 2cc636a

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-05-12 22:41 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ ee6d310

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm ✅ 375/375
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-05-12 23:27 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ f419a22

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm ✅ 375/375
PHPUnit
Newman ⏭️
Playwright ⏭️

Coverage: 0% (0/10 statements)


Quality workflow — 2026-05-13 04:40 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ df3f275

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm ✅ 376/376
PHPUnit
Newman ⏭️
Playwright ⏭️

Coverage: 0% (0/10 statements)


Quality workflow — 2026-05-13 04:47 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ 77ae22a

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm ✅ 376/376
PHPUnit
Newman ⏭️
Playwright ⏭️

Coverage: 0% (0/10 statements)


Quality workflow — 2026-05-13 05:46 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ 9715dd5

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm ✅ 376/376
PHPUnit
Newman ⏭️
Playwright ⏭️

Coverage: 0% (0/10 statements)


Quality workflow — 2026-05-13 07:50 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ d3fa104

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm ✅ 376/376
PHPUnit
Newman ⏭️
Playwright ⏭️

Coverage: 0% (0/10 statements)


Quality workflow — 2026-05-13 09:13 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ 3ff1dbf

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm ✅ 376/376
PHPUnit
Newman ⏭️
Playwright ⏭️

Coverage: 0% (0/10 statements)


Quality workflow — 2026-05-18 18:19 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ 25e72bc

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm ✅ 376/376
PHPUnit
Newman ⏭️
Playwright ⏭️

Coverage: 0% (0/10 statements)


Quality workflow — 2026-05-18 18:56 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ effa7eb

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm ✅ 376/376
PHPUnit
Newman ⏭️
Playwright ⏭️

Coverage: 0% (0/10 statements)


Quality workflow — 2026-05-18 19:15 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ fac6d7f

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm ✅ 376/376
PHPUnit
Newman ⏭️
Playwright ⏭️

Coverage: 0% (0/10 statements)


Quality workflow — 2026-05-18 20:46 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ f1e6e24

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm ✅ 376/376
PHPUnit
Newman ⏭️
Playwright ⏭️

Coverage: 0% (0/10 statements)


Quality workflow — 2026-05-18 21:00 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/larpingapp @ 339eb74

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 94/94
npm ✅ 376/376
PHPUnit
Newman ⏭️
Playwright ⏭️

Coverage: 0% (0/10 statements)


Quality workflow — 2026-05-18 21:17 UTC

Download the full PDF report from the workflow artifacts.

Conduction Release Bot and others added 30 commits August 9, 2026 21:57
…endpoints

hydra gate-25 at package 651e5c5, full tree:

    [gate-25] contract-coverage: FAIL - 2 new public endpoint(s) missing a contract test
    preferences#getPreference - new public endpoint (url=/api/preferences/{key})
    preferences#setPreference - new public endpoint (url=/api/preferences/{key})

Both are consumed by shared @conduction/nextcloud-vue widgets across apps
(CnSupportDialog's 'seen' flag is the current caller), so their wire contract
is not private to this app and had no test at all.

Ten tests covering every branch of both endpoints, asserting the ITEM rather
than the container - the exact status code AND the exact body, plus the exact
IConfig call the controller must make:

  - anonymous read/write -> 401 {message: 'Not logged in'}, and IConfig is
    never touched (expects(never)), so a regression that read config before
    checking the session is caught rather than merely returning the right code
  - a key that sanitises to nothing -> 400 {message: 'Invalid key'}, IConfig
    untouched
  - a stored value -> 200 {value: <stored>} read from pref_<key> for THIS uid
  - an unset key -> {value: null}, not '' - the widgets distinguish 'never set'
    from 'set to empty'
  - a write stores under pref_<key> and echoes the value
  - an EMPTY value deletes rather than storing '', or the next read could not
    tell cleared from empty
  - key sanitisation asserted on the value actually handed to IConfig
    ('../USER_lang!' -> 'pref_userlang'), on both the read and the write path.
    That is a security boundary, not formatting: the pref_ namespace is what
    stops a caller addressing arbitrary IConfig user values of this app.

Can-fail verified by mutating the clear-semantics branch
(if ($value === '') -> a literal that never matches):

    1) ...PreferencesControllerContractTest::testSetPreferenceWithAnEmptyValueDeletesTheKey
    FAILURES! Tests: 10, Assertions: 23, Failures: 1.

restored, git diff lib/ empty, and the full unit suite is OK (195 tests, 639
assertions).

gate-25 goes FAIL - 2 -> PASS on the same tree with the same package.
…rt it creates nothing

`Integration Tests (Newman)` has been red on development and on every open PR.
One assertion of 48:

    1.  AssertionError  anonymous OR write rejected 401
                        expected response to have status code 401 but got 403
        inside "1. Character (OR object CRUD, ADR-022) / Create character AUTHZ:
        anonymous write returns 401"

The 401 was the wrong model, not a regression. OpenRegister's
`ObjectsController::create()` is `#[PublicPage]` on purpose — it is what public
forms post through, which is why it also carries `#[AnonRateLimit]`. Nextcloud's
auth middleware therefore lets an anonymous request reach the controller, and
the refusal comes from OpenRegister's own RBAC afterwards. That is an
authorization failure on a request that was allowed to be anonymous: 403
Forbidden. 401 would mean "authenticate and try again", which this endpoint is
not saying.

Measured directly against a running instance rather than inferred from the CI
log:

    STATUS=403
    {"error":"User 'Anonymous' does not have permission to 'create' objects
      in schema 'Character'"}

The security property the case exists for is unchanged and still strict — the
write is denied. What changed is that the test no longer asserts only a status
code. The status is the weaker half; a denial that nonetheless created a
character would have satisfied any status assertion while being the exact bug
this case is here to catch. So it now also asserts the response carries no
object id and no `@self.id`, and that the code is >= 400.

Verified on the same instance that the denial body carries neither key.
…sts-ed6daf19

test(preferences): wire-contract tests for the two public preference endpoints (gate-25)
The note explaining why `ownerRef` carries no $ref was placed INSIDE
`character.properties`, as a sibling of `ownerRef`. That declares a schema
property named `_note` whose value is a string, where JSON Schema requires a
schema object — and it is the only `_note` at properties level in any register
file in this repo, so there was no precedent making it safe.

OpenRegister walks that map in `Schema::getSchemaObject()`:

    foreach ($this->properties ?? [] as $propertyName => $property) {
        if (($property['properties'] ?? null) !== null) { ... }
        ...
        foreach ($property as $key => $value) { ... }

`$property['properties'] ?? null` is harmless on a string (isset() on a
non-numeric string offset is false, so it yields null), but the later
`foreach ($property as ...)` is not. Reproduced on php:8.3 with the exact
shape:

    Warning: foreach() argument must be of type array|object, string given

Not fatal, but it emits that warning wherever the character schema object is
built, and it puts a non-property into a property map.

Moved to schema level, a sibling of `version` and `properties`, which is what
it actually annotates — the schema's deliberate exception, not a field of it.
Text unchanged; the diff is the one line moving.

Verified after the move: `validate-register` PASS, `validate-json-strict` PASS,
gate-51 schema-property-titles PASS, gate-46 PASS, and gate-54 still reports
exactly the one documented `ownerRef` finding — the note's placement was never
what suppressed anything.
…d6daf19

test(newman): an anonymous OR write is denied 403, not 401 — and assert it creates nothing
…n-refs

fix(specs): resolve the dangling @SPEC anchor and give attendance/portal relations a canonical $ref
…ndings

fix(gates): close two findings the full-tree run reported as PASS
…d6daf19

fix(security): the sentence saying @NoCSRFRequired was removed IS the annotation — CSRF was never enforced on either settings write
Hard pin to the vue3 dist-tag head (2.2.0-vue3.7), up from 2.2.0-vue3.3.

Verified:
- lockfile control run first with the pin unchanged: 0-line diff, so the
  8-line lock change below is attributable to this bump alone
- installed version read off disk after npm ci: exactly one copy,
  2.2.0-vue3.7, peer vue ^3.5.0
- build: exit 0, 2 warnings before and after (unchanged)
- unit tests: 5 files / 40 tests passed before and after
- bundle: 63,847,011 -> 63,876,361 bytes (+29,350, +0.05%)

No caret: ^2.2.0 does not match a prerelease, and the latest/beta
dist-tags are the retired Vue 2 lineage.
chore(deps): pin @conduction/nextcloud-vue to 2.2.0-vue3.7
Follow-up to #303, which pinned 2.2.0-vue3.7. The vue3 dist-tag moved
twice more while the fleet wave was running (vue3.7 -> vue3.8 -> vue3.9),
because every merge to nc-vue's feat/vue-3 cuts a publish. The fleet
converges on 2.2.0-vue3.9.

Verified on npm 10.8.2, the version CI runs:
- control (pin unchanged): 0-line diff
- npm ci: exit 0
- installed off disk: one copy, 2.2.0-vue3.9, peer vue ^3.5.0
- build: exit 0, 2 warnings at 2.2.0-vue3.7 and at 2.2.0-vue3.9
- vitest: 5 files / 40 passed at both versions
- bundle: 63,876,359 -> 63,887,521 bytes (+11,162, +0.02%)

2.2.0-vue3.9 is not pre-verified against our apps the way 2.2.0-vue3.7
was, so the run above is the verification. No regression.
chore(deps): pin @conduction/nextcloud-vue to 2.2.0-vue3.9
`workflow_dispatch` was absent, and this repo and one other were the only two
of the sixteen fleet apps where that was true — checked by reading
.github/workflows/code-quality.yml on `development` in all sixteen.

The consequence was not inconvenience. Every fleet-wide gate sweep in the
current quality programme is a workflow_dispatch fan-out, so this repo was not
failing those sweeps and was not passing them: it was absent from the results
table entirely, which in a table of fourteen verdicts is indistinguishable from
a repo that was never a problem.

A dispatch is also strictly more informative than a re-run of CI here. The
shared quality workflow scopes workflow_dispatch to the FULL repository, because
there is no pull-request target branch and no previous pushed tip to diff
against, so ADR-020 diff-scoping has nothing to scope to. A push run on
`development` typically covers one commit's files; this is the only way to ask
what the state of the whole app is without opening a pull request. Expect the
first dispatch to be redder than a PR — that is the honest answer, not a
regression.

MEASURED, not assumed: dispatch does NOT require the trigger on the default
branch. nldesign's default branch is `main`, its `main` carries no
workflow_dispatch, and its dispatch run 31393755672 on `development` fired
regardless. Landing this on `development` is therefore sufficient.
…disproven, see below) (#306)

* fix(gates): close gate-46 and gate-54 at full scope

Two findings that a full-scope `workflow_dispatch` reports and a diff-scoped
PR never showed. Both are real; neither is closed by relaxing a check.

gate-46 spec-anchor-existence (1 -> 0)
-------------------------------------
`tests/validate-manifest.js` carried a @SPEC tag pointing into an OpenRegister
CHANGE directory. Three things were wrong with it at once: it is a change dir
rather than a canonical spec; the path has never existed in THIS repository, so
it could not resolve here however the gate was written; and OpenRegister itself
archived that change on 2026-05-27 as superseded, so it no longer resolves
there either. The requirement it names is real — REQ-OR-MAN-007, canonically at
openspec/specs/openregister-app-manifest/spec.md in ConductionNL/openregister —
and it has exactly one home, which is not larpingapp. Copying it into
larpingapp/openspec/specs/ purely to make a tag resolve would manufacture a
second copy of an existing spec, so the pointer is kept as prose naming the
owning repo and is no longer asserted as a machine-checkable claim about this
tree. The surrounding comment also described OpenRegister's wiring verbatim
("OpenRegister is the foundation app and ships no manifest yet") — larpingapp
does ship a manifest and reaches this validator through the check:specs
aggregate, so that is corrected too.

gate-54 relation-dialect (1 -> 0)
---------------------------------
`character.ownerRef` is genuinely a relation to `player` and now declares
`$ref: "player"` per ADR-062 rule 7, together with `visible: false` and
`readOnly: true`.

The second half is why the first is finally safe. ef7346c removed this exact
$ref because adding it alone broke the character create dialog — ocName already
declares `$ref: "player"`, and a second player-targeted relation made
`character create dialog exposes a player (ocName) selector` fail on
`input[placeholder*="player" i]`, twice. That measurement stands for that arm.
It does not carry to this one: nc-vue's `fieldsFromSchema()` drops
`visible: false` / `readOnly: true` properties as its FIRST filter, before any
$ref is read or any selector resolved, so a hidden property cannot contribute a
second selector to the dialog.

The visibility is correct on its own merits and is not a gate dodge. ownerRef is
the portal-subject scoping key, stamped server-side by PortalObjectWriter and
projected out of the player view; it was never meant to be typed by a user. Its
direct sibling `ownerUid` has carried `visible: false` + `readOnly: true` in
larpingapp_register.json all along, so this aligns the two.

Evidence, full-scope `hydra-gates --full` against the whole tree:
  before  gate-46 FAIL 1 · gate-54 FAIL 1 · gate-19 FAIL 50   (3 gates red)
  after   gate-46 PASS   · gate-54 PASS   · gate-19 FAIL 50   (1 gate red)
gate-16 spec-coverage stays PASS across both, so nothing was traded for it.

gate-46 was additionally shown to be live rather than merely quiet: an
intermediate revision of this commit quoted the dangling target verbatim inside
the comment explaining it, and the gate — which reads the file as text — went
straight back to FAIL 1 on that prose. Removing the literal returned it to PASS.
A planted true positive, if an unintentional one.

gate-19 (50 e2e-coverage findings) is untouched here and stays red on purpose;
it needs real Playwright tests, not an annotation pass.

* revert(gate-54): the ownerRef $ref breaks the dialog with `visible:false` too

My hypothesis was wrong and the e2e says so. Reverting the gate-54 half of
2bd48f3; the gate-46 half is unaffected and stays.

WHAT I CLAIMED. Adding `$ref: "player"` to `character.ownerRef` alone broke the
character create dialog (ef7346c, measured twice on #289). I argued that adding
it TOGETHER WITH `visible: false` + `readOnly: true` would be safe, because
nc-vue's `fieldsFromSchema()` drops non-visible/readOnly properties as its FIRST
filter — before any $ref is read or any selector resolved — so a hidden property
could not contribute a second player selector.

WHAT HAPPENED. Run 31428015957, Playwright:

  ✘ tests/e2e/spec-coverage/detail-forms-admin.spec.ts:570
    'character create dialog exposes a player (ocName) selector'
    expect(locator).toBeVisible() — element(s) not found
    1 failed / 170 passed (12.4m)

The same test, the same locator, the same 1/170 shape as the arm this was
supposed to avoid. Every other job on the PR passed.

WHAT THAT ACTUALLY TELLS US — and it is worth more than the fix would have been.
The claim about `fieldsFromSchema()` is TRUE; the conclusion drawn from it is
not. Whatever resolves the player selector does not go through the visibility
filter at all, so the collision between `ocName` and `ownerRef` is at SCHEMA
level rather than form-field level, and hiding the property cannot reach it.
That rules out an entire family of remediations rather than just this one.

The `_note` on the property now records BOTH measured arms, names the run for
each, and says explicitly not to try a third variant of the same idea — the next
real attempt is upstream, keying relation resolution by PROPERTY rather than by
target schema slug so two properties may point at `player` at once.

gate-54 therefore returns to FAIL 1 on purpose. Verified locally at full scope:
  with the $ref     gate-54 PASS
  without it        gate-54 FAIL 1
so the gate is live in both directions; it is the remediation that is blocked,
not the finding that is false.

---------

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

The last non-coverage finding. Two previous arms added this exact $ref and both
were reverted because one Playwright test went red. That test was wrong, and
this commit shows it rather than asserting it.

WHAT THE TEST ACTUALLY CHECKED
  test('character create dialog exposes a player (ocName) selector')
    expect(dialog.locator('input[placeholder*="player" i]')).toBeVisible()

CnFormDialog passes `:placeholder="field.description"` ONLY on its text/email/
url and number branches. The NcSelect branch that renders a `$ref` relation is
given `:input-id` and `:input-label` and NO placeholder. So that locator could
never match a relation picker at all. The single element it did match in the
Add Character dialog was `ownerRef`'s PLAIN TEXT BOX, whose description reads
"UUID reference to the player object".

The assertion was therefore the exact inverse of its own name: green precisely
while ocName was NOT a selector, red the moment a $ref made it one. gate-54's
remediation and this test were mutually exclusive by construction, which is why
both previous arms failed with the identical 1-failed/170-passed shape — not
because of anything they had in common with each other, but because both remove
the text box the assertion was really reading.

It slid onto ownerRef with no red window, so nothing flagged it:
  987c9ee  test written; ocName is a plain string, no $ref, no selector — the
            assertion passed BECAUSE there was no selector
  f33fc0b  ownerRef added, plain text, description mentioning "the player
            object" — a second matching input
  f434f76  ocName gains `$ref: "player"`, becomes a select and LOSES its
            placeholder; the suite stayed green only because ownerRef had
            arrived two days earlier and inherited the match

THE UPSTREAM FIX THAT WAS PROPOSED IS A NO-OP
The previous note concluded the collision was "at SCHEMA level" and asked for
nc-vue to key relation resolution by PROPERTY rather than by target schema slug.
nc-vue already does: the async option state, the search handler and the enum
cache are each keyed `field.key`; `referenceLabels` is keyed by UUID (shared by
design, and harmless — identical labels). Nothing in the library is keyed by
`field.reference.schema`. Two properties may already point at `player` at once.
The proposed change would have implemented behaviour that already exists.

WHAT THIS COMMIT DOES
- `character.ownerRef` declares `$ref: "player"` with `visible: false` +
  `readOnly: true`. The visibility is correct on its own merits, not a gate
  dodge: ownerRef is stamped server-side by PortalObjectWriter and projected out
  of the player view, and its sibling `ownerUid` has carried both flags all
  along.
- The assertion now targets `#cn-form-ocName` — the id CnFormDialog gives every
  select — so the test finally checks the selector its name promises, and fails
  if that picker stops rendering. This is a correction, not a tightening, and
  not a weakening: the old assertion could not fail for the reason it claimed.
- The `_note` records the corrected diagnosis and withdraws the prohibition.

BOTH DIRECTIONS PROVEN, canonical helper from ConductionNL/.github@main
(78d882a — the ref CI resolves), against all 7 register files:
  before                     gate-54 FAIL 1
    character.ownerRef — relation-shaped property lacks canonical $ref
  after                      gate-54 PASS 0
  planted TP ($ref removed)  gate-54 FAIL 1, naming character.ownerRef again
  reverted                   gate-54 PASS 0

gate-19 (50 e2e-coverage findings) is untouched and stays red on purpose; it
needs real Playwright tests, not an annotation pass.
… feature + close the game-mechanics gate-19 cluster (50 → 42) (#311)

* fix(listeners): two defensive guards that silently disabled their own feature

Both of LarpingApp's OpenRegister listeners were registered, reached, and
inert — each stopped by a guard on its first line.

1. CharacterRequirementListener (larpingapp#308)
   `isCharacterSchema()` probed `method_exists($entity, 'getSchema')`.
   `OCA\OpenRegister\Db\ObjectEntity` declares only `getObject()`; every
   other accessor is resolved by `OCP\AppFramework\Db\Entity::__call()`.
   Measured live (NC 34, openregister 0.2.17-unstable.36):

       method_exists($entity, 'getSchema')  -> false      (magic)
       is_callable([$entity, 'getSchema'])  -> true
       $entity->getSchema()                 -> "17"
       method_exists($entity, 'getObject')  -> true       (declared — control)

   So the method returned false for EVERY character write, `handle()`
   returned early, and no veto was ever raised. Black-box on a single-owner
   rig, before -> after, one line changed:

       POST a character carrying a skill whose requiredSkills[] is unmet
           HTTP 201, persisted   ->   HTTP 422, itemised requirements
       PUT adding that skill to a clean character
           HTTP 200, persisted   ->   HTTP 422
       POST a character with no unmet requirement (control)
           HTTP 201              ->   HTTP 201

   larpingapp#308 attributed this to OpenRegister never dispatching the
   vetoable pre-write event. It does dispatch it: `ObjectCreatingEvent`
   carries 7 listeners on this instance and CharacterRequirementListener is
   first in the chain. The issue's grep landed on `SaveObjects.php` (plural,
   the bulk path) rather than `SaveObject.php` (singular, the REST path).

2. DeepLinkRegistrationListener
   The guard probed `method_exists($event, 'registerDeepLink')`.
   `DeepLinkRegistrationEvent` has never declared such a method — the API is
   `register($appId, $registerSlug, $schemaSlug, $urlTemplate, ...)`. The
   guard was false on every dispatch, so zero deep links were registered;
   dropping the guard without renaming the call would have fatalled with
   "Call to undefined method". The guard was the only thing hiding a broken
   call. Verified live: 0 registrations -> 8.

   Also corrects two map keys. `item` and `event` collide instance-globally,
   so LarpingApp's schemas are `larping_item` / `larping_event`; the bare
   spellings matched no schema on any instance.

Why the unit suites were green over both
----------------------------------------
Each suite's hand-written fake was shaped to what the caller CALLS rather
than to what the collaborator IS:

  - `FakeObjectEntity` DECLARED `getSchema()`, so `method_exists()` answered
    true in the suite and false in production. It now mirrors the real class:
    `getObject()` declared, `getSchema()` via `__call()`.
  - The deep-link fakes declared a three-argument `registerDeepLink()` — an
    API that existed nowhere but in that file. They now mirror the real
    `register()` signature exactly.

Negative control, expectation computed first: with the faithful fake and the
`isCharacterSchema()` fix reverted, exactly the three rejection-asserting
tests go red (`testRejectsCreateWithUnmetPrerequisite`,
`testOverrideRejectedFromNonGm`, `testEmptyReasonOverrideRejected`).
Restored: 201 tests, 683 assertions, `composer check:strict` ALL PASSED.

Also fixes the app's only two pre-existing Psalm findings (`UndefinedMethod`
on `getNewObject()`/`getOldObject()` in `extractEntities()`), present on the
pristine tree before this change.

* test(game-mechanics): close the 8-scenario gate-19 cluster with real numeric assertions

gate-19 unscoped (CI's `--full` invocation): **50 -> 42**, with all eight
`game-mechanics::` findings gone and nothing else moved. Expectation computed
before the run.

Every assertion is a number derived in advance from seeded data and compared
against what the real `CharacterService` computes from rows really persisted
through OpenRegister's object REST API. Nothing asserts merely that a write
was accepted — that shape is why the previous attempt at the
`skill-requirement-enforcement` cluster was correctly abandoned.

Driven through the genuine production path — `calculateCharacter()` /
`calculateAllCharacters()` with their own `loadAllEntities()`/findAll loader,
no reflection injection, no stubbed collections:

  initializeAbilityScores -> applyEntityEffects (skills, items, conditions,
  events) -> applyEffects -> calculateEffect -> applyModifierToAbility

NEGATIVE CONTROLS — three engine mutations, each prediction written before
the run, each matched exactly
--------------------------------------------------------------------------
| plant (source, not test)                            | predicted | observed |
|-----------------------------------------------------|-----------|----------|
| A `applyModifierToAbility` positive branch SUBTRACTS | 6 F / 2 P | 6 F / 2 P |
| C the non-cumulative dedup rule can never match      | 1 F / 7 P | 1 F / 7 P |
| E `initializeAbilityScores` seeds value 0, not base  | 7 F / 1 P | 7 F / 1 P |

Plant A leaves `ability serialises` green (it carries no effects) and
`condition applies a negative modifier` green (the `negative` branch is
untouched) — both are covered by plant E. Plant E leaves `cumulative and
non-cumulative` green because both its abilities are deliberately seeded at
base 0, so it is base-independent by construction; plant C is what covers it.
Every one of the eight fails under at least one mutation. All plants reverted;
8/8 green afterwards.

HARNESS
-------
- `computeStatsLive()` returns the WHOLE derived `stats` block. The scenarios
  are about the SHAPE of the derivation — an untouched ability keeping an
  empty audit, four carriers composing onto one ability in order, a
  non-cumulative effect applying exactly once — and none of that can be
  asserted one ability at a time.
- `computeRosterLive()` drives `calculateAllCharacters()`, the batch entry
  point the roster-independence scenario is actually about.
- `NC_CONTAINER` makes the docker path target a named rig. Previously, on a
  developer box, `findServerRoot()` walked up to the SHARED dev container's
  server root and ran the derivation against ITS database while the HTTP
  fixtures were written to an isolated rig — the two halves of every assertion
  talking to different instances, failing with `base: null`, which reads
  exactly like broken arithmetic. Unset, behaviour is unchanged, CI included.

Isolation: `initializeAbilityScores()` walks EVERY ability in the register, so
every assertion is keyed on the UUID of an ability seeded under this run's own
`RUN_ID` prefix, never on the size of the stats block.

* fix(deps): close CVE-2026-65954 — phpcsstandards/phpcsutils 1.2.2 -> 1.2.3

`quality / Security (composer)` has been red on `development` since at least
run 31461764863 (2026-08-11 05:28Z), on a pre-existing advisory this branch
did not introduce — proof: `git diff origin/development -- composer.json
composer.lock` was EMPTY when the cell first went red on this PR.

    Advisory PKSA-kh6k-gs3g-dgr6 / CVE-2026-65954
    Arbitrary code execution, PHPCSStandards/PHPCSUtils
    affected >=1.0.0-alpha1,<1.2.3 — installed 1.2.2

Bumped narrowly (no `--with-dependencies`, so nothing else in the tree moves):
`composer audit` -> "No security vulnerability advisories found",
`composer check:strict` -> ALL CHECKS PASSED, 201 tests / 683 assertions.

⚠️ Note for anyone running the suite locally: `composer.json` sets
`config.platform.php = 8.3` and `require.php = ^8.3`, so a plain `composer
install` on a PHP 8.2 box regenerates `vendor/composer/platform_check.php` and
every PHPUnit run then dies with exit 255 before collecting a single test —
a failure that names the platform, not the change. Use
`composer install --ignore-platform-reqs` there. CI runs 8.3/8.4 and is
unaffected.

* docs(spec-anchors): repoint 78 dangling @SPEC anchors to their canonical specs (larpingapp#309)

`opsx-archive` moved 32 changes under `openspec/changes/archive/` and left every
annotation behind. Measured by a resolver over every tracked `.php/.ts/.js/.vue`,
with a positive control built in (it refuses to report unless anchors resolve):

|                      | before | after |
|----------------------|--------|-------|
| RESOLVING `@spec`    | 38     | **116** |
| RESOLVING `@e2e`     | 114    | 122   |
| DANGLING total       | 374    | **296** |
| DANGLING `@spec`     | 351    | **273** |

This commit takes only the MECHANICAL subset: anchors of the shape
`openspec/changes/<change>/specs/<Y>/spec.md` repointed to
`openspec/specs/<Y>/spec.md`, and only where that canonical target was verified
to exist first. 78 anchors, 21 sites, 19 files, 11 distinct path pairs.

⚠️ TWO THINGS DELIBERATELY NOT DONE
-----------------------------------
**1. The 22 `@e2e` anchors of the same shape are left dangling on purpose.**
All 22 live in `tests/e2e/spec-coverage/spa-ui.spec.ts` and all point at the five
`*-leaf` specs — features that are UNBUILT (open issues #184#188, #302).
larpingapp#301 already establishes that retargeting their inert tags manufactures
a false green: a resolving `@e2e` is scored as coverage by gate-19 whether or not
the test exercises the scenario. Repointing them would drop the gate-19 number
without a single new assertion existing. They stay dangling until the leaves are
built. The split is clean — every one of the 78 moved here is `@spec`, and no
file mixes the two for the same path.

**2. The 273 remaining dangling `@spec` anchors are NOT mechanically fixable.**
255 of them (235 + 15 + 5) target a `tasks.md#task-N` inside a retrofit change
directory — the wrong KIND of artifact as well as a dead path, since `@spec` must
target canonical `openspec/specs/`. Each needs a per-tag judgement about which
requirement it describes, so they are left for a change that can make that
judgement rather than guessed at in bulk.

Verified unchanged by this commit, on the same tree: gate-19 **42** (no `@e2e`
anchor moved), gate-16 `# count=0`, `composer check:strict` ALL PASSED,
201 tests / 683 assertions.
The PHP Quality (psalm) cell reported success while Psalm was disabled in the caller workflow, because the shared workflow still creates a job under that name for a disabled tool. The reason it was disabled was a broken stub path in psalm.xml pointing at an OCP.bak directory that only one developer's container-symlinked vendor tree ever had. Path corrected, cell enabled. This PR's own run shows Psalm reaching a clean result with 93.05 percent of the codebase inferred, matching the local measurement exactly. Also unguards the composer psalm and phpstan scripts so a missing binary fails loudly instead of greening the cell. Note the correction posted in the comments: the 142-byte artifact I originally cited is not evidence of anything.
* 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.
….prettierrc (#314)

The phpcs migration (#313) pointed phpcs.xml at
vendor/conduction/hydra-gates/quality-config/phpcs.xml and left the other
root configs behind. This does the same for PHPMD.

- phpmd.xml becomes the stub from quality-config/stubs/phpmd.xml.
- phpmd-unusedparams.xml is deleted; the second leg of the `phpmd` composer
  script now points at the central copy. Both legs are kept, as is the
  worst-exit-code behaviour.
- .prettierrc is deleted. Nothing depends on prettier (no package.json dep, no
  script, no workflow reference), so it is inert in CI — but it is live in
  editors, where it tells Prettier to format .ts with 2-space indent and double
  quotes, both of which @nextcloud/eslint-config then flags.

Verified rather than assumed. Both legs were run in php:8.3-cli before and
after, and the EFFECTIVE ruleset was dumped rule-by-rule (class, name,
priority, every property) so a stub that quietly narrowed scope could not read
as a clean pass:

  effective ruleset : 43 rules before, the same 43 after, byte-identical
  composer phpmd    : identical output, exit 0 both times

PHPStan is deliberately NOT in this commit. quality-config/phpstan-base.neon
declares its paths relative, and PHPStan resolves those against the config file
that declares them, so from vendor/ it looks for
vendor/conduction/hydra-gates/quality-config/lib. Adopting the stub aborts the
run outright. Fixed in ConductionNL/.github#387; this app follows once that is
released.
#315)

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).
phpstan.neon now includes the shared base shipped in
conduction/hydra-gates (quality-config/phpstan-base.neon) and keeps only
what is genuinely local to this app: its own baseline include and the two
ignores naming symbols no other fleet app has.

Requires hydra-gates v1.7.1 — v1.7.0's base declared bare relative paths,
which PHPStan resolves against the file that declares them, so the run
aborted before analysing anything. composer.lock is updated accordingly;
no other package moved.

Verified with phpstan dump-parameters before and after: level, paths,
excludePaths, bootstrapFiles and scanDirectories resolve byte-identically,
28 files analysed on both sides, no findings on either.
Moves the pin from 2.2.0-vue3.9 to the current vue3 dist-tag.

The lockfile was regenerated with npm 10.8.2 to match the npm version CI
runs (engines: npm ^10.0.0); npm ci was verified from a clean
node_modules.

Verified locally: npm ci, build, 5 vitest files / 40 tests via
'npm run test:unit' (this repo has no plain 'test' script), eslint,
stylelint — all pass.

Co-authored-by: Ruben van der Linde <juan.claude@conduction.nl>
…318)

Lock-only. composer.json is untouched: the ^1.0 constraint is correct and
stays floating.

v1.7.3 removes two conditional paths from the shared phpstan-base.neon
(%cwd%/vendor-bin and %cwd%/lib/Resources/template). A conditional path in a
shared base has no spelling that is safe on both PHPStan majors: plain is
validated and ABORTS on PHPStan 2.x, the '(?)' marker is parsed as a NEON
entity after a %...% expansion and crashes 2.x, and quoting it stops 1.x from
stripping the marker so the exclusion silently matches nothing.

This app is on PHPStan 1.12.x, so it is not broken today, but it carries the
landmine until it moves to PHPStan 2. It has neither vendor-bin nor
lib/Resources/template, so no phpstan.neon change is needed.
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.

3 participants