Skip to content

Release: merge development into beta - #3204

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

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

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

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

Merging this PR will trigger the beta release workflow.

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

github-actions Bot and others added 6 commits August 31, 2026 10:24
The 2.0.6-beta.20260831102212 release bumped the version on beta. Without this,
development stays behind beta and the next development -> beta promotion
conflicts on the version file.

Version files resolve to development's side, which is the higher line,
so this never moves a version backwards.
Dependabot cannot propose composer updates for this app. Its updater fails
with:

  Your requirements could not be resolved to an installable set of packages.
    - edgedesign/phpqa[v1.27.0, ..., v1.27.2] require ext-xsl * ->
      it is missing from your system.

config.platform pins php 8.3 so composer resolves against a known PHP version,
but says nothing about extensions. edgedesign/phpqa requires ext-xsl and the
resolving environment does not have it, so the resolve fails before any bump
can be computed.

CI is unaffected, which is why this went unnoticed: composer install replays
the committed lock and never re-resolves, so the pipeline stays green while
dependabot -- which does re-resolve -- fails every time. The shared quality.yml
installs no xsl extension anywhere and never invokes phpqa.

Declaring ext-xsl beside the php pin makes resolution assume exactly what the
committed lockfile already assumes. Verified in a clean composer:2 container
without --ignore-platform-reqs: the same file fails without this line and
resolves (103 installs, lock written) with it.

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
The 2.0.7 release bumped the version on main. Without this,
development stays behind main and the next development -> main promotion
conflicts on the version file.

Version files resolve to development's side, which is the higher line,
so this never moves a version backwards.
….0.7

chore(release): sync main back into development
….0.6-beta.20260831102212

chore(release): sync beta back into development
Levels this app with the fleet. 2.27.2 adds two fixes the earlier 2.27.0 pin does
not carry:

  * headerless is no longer chromeless — a flat KPI card in a borderless wrapper
    had no card, border or background at all;
  * a stat `variant` paints from the `-text` tokens rather than the fill tokens,
    which failed WCAG AA at 1.08:1 as a foreground colour. The KPI colour
    cleanup converted hardcoded values to `variant`, so this is what makes those
    conversions contrast-safe.

Lockfile only, and npm pruned nothing.
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ bcd3452

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

Quality workflow — 2026-08-31 11:40 UTC

Download the full PDF report from the workflow artifacts.

dependabot Bot added 2 commits August 31, 2026 13:55
Bumps [zod](https://github.com/colinhacks/zod) from 4.4.3 to 4.5.2.
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](colinhacks/zod@v4.4.3...v4.5.2)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 4.5.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [marked](https://github.com/markedjs/marked) from 12.0.2 to 18.0.11.
- [Release notes](https://github.com/markedjs/marked/releases)
- [Commits](markedjs/marked@v12.0.2...v18.0.11)

---
updated-dependencies:
- dependency-name: marked
  dependency-version: 18.0.11
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ 972d1ea

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

Quality workflow — 2026-08-31 12:14 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ 556155b

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

Quality workflow — 2026-08-31 12:46 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ 33fd1cd

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

Quality workflow — 2026-08-31 13:15 UTC

Download the full PDF report from the workflow artifacts.

#3220)

The override read:

  "apexcharts": "License is MIT - license-checker misreads logo URL as custom
   license"

That was true once and is not true now. apexcharts stopped being MIT at
6.0.0: 4.7.0 and 5.0.0 are MIT, 6.0.0 and 7.0.0 are SEE LICENSE IN LICENSE.
This app declares ^7.0.0 and locks 7.0.0, so the checker flagging it is
CORRECT and the override was suppressing a true finding rather than
correcting a false one -- the licence gate was reporting green on a
dependency that is not open source.

Nothing about the dependency changes here. apexcharts 7 stays. What changes
is that the file now says what is actually true:

- 6.0.0+ ship a dual-license: free under a Community License for
  organisations below USD 2M annual revenue, paid above it, and sublicensing
  under different terms is not permitted.
- Conduction confirmed 2026-08-31 that it is below that threshold and so
  qualifies under the Community License.
- The override is therefore a deliberate acceptance, dated and attributed,
  not a claim that the checker is wrong.

The note also records the redistribution question, because the Community
License binds the organisation USING the software rather than only the one
shipping it, and this app is distributed through the Nextcloud App Store. An
installing tenant above USD 2M would need its own commercial licence. That is
flagged for a decision, not decided here.

For reference: dossiq is on apexcharts 4.7.0 (MIT) with no override, and its
bump to 7.0.0 was closed today on these grounds (dossiq#1527). apexcharts 5.x
is the last MIT release if the fleet ever needs to return to one.

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

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ fca2265

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

Quality workflow — 2026-08-31 13:48 UTC

Download the full PDF report from the workflow artifacts.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ d630b0c

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

Quality workflow — 2026-08-31 14:34 UTC

Download the full PDF report from the workflow artifacts.

github-actions Bot and others added 3 commits August 31, 2026 15:01
The 2.0.9 release bumped the version on main. Without this,
development stays behind main and the next development -> main promotion
conflicts on the version file.

Version files resolve to development's side, which is the higher line,
so this never moves a version backwards.
…0260831154312

chore(sync): carry beta back into development
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ 0ae5e0a

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

Quality workflow — 2026-08-31 16:06 UTC

Download the full PDF report from the workflow artifacts.

Dependabot cannot update these packages. Its npm updater fails with:

  npm error code EOVERRIDE
  npm error Override for <pkg> conflicts with direct dependency

The package is listed BOTH as a direct dependency and as a literal overrides
entry pinned to the same range. Dependabot bumps the direct dependency and
does not move the override, npm rejects the mismatched pair, and the update
is abandoned -- so these packages can never be updated, security advisories
included.

npm's self-reference is the idiom for exactly this: $<name> tells the
override to follow whatever the direct dependency resolves to, instead of a
range that has to be kept in lockstep by hand. This repo already uses it (see
the vue entry under @vue/test-utils).

Transitive consumers still get pinned to one version -- the direct
dependency's -- which is what the override was there to do. What changes is
that the pin tracks the dependency instead of duplicating it.

Verified in a clean container: bumping the direct dependency with the literal
override reproduces the EOVERRIDE above; with the self-reference the same
bump resolves.

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

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ 5ddb1ca

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

Quality workflow — 2026-08-31 16:45 UTC

Download the full PDF report from the workflow artifacts.

…SetupHandler (#3233)

* fix(db): bind catch(Exception) to a class that exists, and drop dead SetupHandler

Two findings that psalm-baseline.xml had been carrying as accepted noise.
Neither is noise. 187 -> 184.

lib/Db/Register.php caught `Exception` with no import, inside namespace
OCA\OpenRegister\Db. PHP resolves an unqualified catch type against the
CURRENT namespace, so that block was bound to OCA\OpenRegister\Db\Exception,
which does not exist, and it could never catch anything. Verified rather
than reasoned about:

    namespace Demo\Sub;
    try { throw new \RuntimeException("boom"); }
    catch (Exception $e) { ... }   // no import
    -> NOT CAUGHT -> RuntimeException: boom

Adding `use Exception;` binds it to the global class the code plainly meant.
Checked the rest of lib/: ObjectsController and Repair/RenameDutchColumns
also catch a bare `Exception`, but both import OCP\DB\Exception, so their
catches bind to a real class and are deliberate. Psalm flagged exactly the
one file that was broken.

lib/Service/SettingsService.php declared a property, a constructor
parameter and two docblocks for SetupHandler, a class that exists nowhere
in the tree. It was written and never read, so it was dead API surface
that would fatal the moment anything tried to supply it. Removed, along
with the `setupHandler: null` argument in Application.php and the matching
positional null in the two tests that build the service positionally.

Verified with the repo own vendor/bin/psalm 5.26.1 on PHP 8.3: errors with
the baseline emptied went 187 -> 184, and the regenerated baseline is green.
PHPUnit was NOT run here: it needs the Nextcloud server bootstrap, which
only exists inside a server checkout.

* fix(tests): realign the five positional constructor calls I missed

Removing the SetupHandler parameter shifted every later POSITIONAL
argument, and I checked for that by reading the first line after each
`new SettingsService(` and concluding the file used named arguments. It
uses both. Five calls further down SettingsServiceTest.php are positional,
and CI caught it as 13 TypeErrors, all the same one:

  Argument #14 ($appName) must be of type string, MockObject_IAppContainer
  given

which is the shift saying exactly what it was.

Verified by position this time rather than by sampling: with the parameter
gone, appName is slot 14, and all seven positional call sites across the
three test files now carry a string literal there.

---------

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

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ 8edc871

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

Quality workflow — 2026-08-31 17:06 UTC

Download the full PDF report from the workflow artifacts.

rubenvdlinde and others added 2 commits August 31, 2026 19:07
….0.9

chore(release): sync main back into development
…etter (#3236)

Two more entries the Psalm baseline was carrying. Both are real. 184 -> 180.

PollLinkService had a fallback that could never run:

    $pollId = (int)$this->db->lastInsertId("oc_polls_polls_id_seq");
    if ($pollId === 0) {
        // Fallback for drivers without sequence support.
        $pollId = (int)$this->db->lastInsertId();
    }

OCP\IDBConnection::lastInsertId(string $table): int takes a REQUIRED
argument, so the no-arg call raises ArgumentCountError on exactly the
drivers the fallback was written for. The surrounding catch (Throwable)
turned that into "Failed to create poll", which blames the insert rather
than the id retrieval. A zero now falls through to the explicit throw that
was already there and says what actually happened.

Checked the other four lastInsertId call sites. MagicMapper and
DedupCollidedSchemasCommand pass a table. DbalObjectSourceProvider calls it
with no argument, but on a DBAL Connection rather than IDBConnection, where
the parameter is optional. Psalm flagged only the one that was wrong.

CaseToken declared getToken TWICE:

    @method string|null getToken()
    @method void getToken(?string $token)   <- copy-paste of the setter
    @method void setToken(?string $token)

Psalm binds to the second, which is why two correct $row->getToken() calls
read as "Too few arguments". Removed; setToken on the next line was already
right. Swept lib/Db for the same slip: this was the only one of them.

Verified with the repo own vendor/bin/psalm 5.26.1 on PHP 8.3: all four
TooFewArguments are gone, errors with the baseline emptied went 184 -> 180,
regenerated baseline is green. PHPUnit was NOT run here, it needs the
Nextcloud server bootstrap that only exists inside a server checkout.

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

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ 6dac523

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

Quality workflow — 2026-08-31 17:13 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ c54b65a

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

Quality workflow — 2026-08-31 17:28 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ 8bfee0d

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

Quality workflow — 2026-08-31 17:58 UTC

Download the full PDF report from the workflow artifacts.

… it (#3243)

* fix(chat): send temperature through modelOptions, where LLPhant reads it

Four assignments of $config->temperature and one of $config->organizationId
on LLPhant\OpenAIConfig. Neither is a property of that class, so each one
created a DYNAMIC property that nothing ever read. An agent temperature
configured in the UI never reached the model.

The suppressions above them said so and were read the other way round:

    @psalm-suppress UndefinedPropertyAssignment LLPhant dynamic properties

LLPhant does not use dynamic properties. It reads modelOptions:

    OpenAIChat.php:89   $this->modelOptions = $config->modelOptions;
    OpenAIChat.php:368  $openAiArgs = $this->modelOptions;

and OpenAIConfig own docblock lists temperature inside the ModelOptions
array shape, not among its properties. Nothing anywhere in the library
reads a temperature property. Temperature now goes to
$config->modelOptions["temperature"], which is what reaches the API.

organizationId is removed rather than relocated: LLPhant has no
organization support of any kind, so there is nowhere for it to go. That
drops the pretence, not the behaviour, because there was none.

⚠️ PHP 8.2 deprecated dynamic property creation, so this was also going to
start emitting deprecations and then fail outright.

Verified with the repo own vendor/bin/psalm 5.26.1 on PHP 8.3: errors with
the baseline emptied went 173 -> 168, regenerated baseline is green. PHPUnit
was NOT run here, it needs the Nextcloud server bootstrap that only exists
inside a server checkout.

* style(chat): start the new inline comments with a capital

phpcs: "Inline comments must start with a capital letter". Five of the
comments I added opened on an identifier (modelOptions, organizationId).

---------

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

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ 77ce235

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

Quality workflow — 2026-08-31 18:26 UTC

Download the full PDF report from the workflow artifacts.

…y holds (#3245)

$this->stats was documented as eight int keys, and the name-cache warm-up
stores a ninth, warmup_breakdown, holding an array. That mismatch is what
Psalm was reporting as InvalidPropertyAssignmentValue, and it had been
baselined rather than reconciled.

The runtime behaviour was never wrong: PHP arrays take the key regardless.
What was wrong is that the declaration described a narrower shape than the
class keeps, so anything reading the annotation, a reader or an IDE or the
analyser, was told the key cannot be there.

Declared optional, because it is absent until the warm-up has run.

Verified with the repo own vendor/bin/psalm 5.26.1 on PHP 8.3: 168 -> 167
with the baseline emptied, regenerated baseline green, phpcs clean on the
touched file.

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

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ 91a6000

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

Quality workflow — 2026-08-31 18:56 UTC

Download the full PDF report from the workflow artifacts.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ d94a33b

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

Quality workflow — 2026-08-31 20:42 UTC

Download the full PDF report from the workflow artifacts.

* feat(schemas): let an app retire a schema from its descriptor

Removing a schema from an app's register descriptor never removed it from
the instance. ImportHandler unions the freshly-imported schema ids into the
register's existing list and prunes only the ids it has just shadowed by
slug, so a retired schema kept its row, its magic table and its place in the
register's schemas array forever. On a shared instance that is how a
cross-app slug collision outlives the descriptor change meant to end it.

Adds occ openregister:schemas:prune-retired --app <appId> --slug <slug>.
Scoped by owning application, so it can never reach a same-slug schema
another app owns: measured on the dev instance, filinq's product is id 21
and decidiq's is id 987. Dry-run by default, refuses a schema that still
owns objects unless --force, and unlinks the id from every referencing
register before the row goes.

unlinkSchemaId() matches the integer and the string form of an id. The
stored list holds either depending on which import era wrote it, so a
strict comparison would leave "74" behind pointing at a deleted row.

* docs(schemas): tag every prune-retired method with its @SPEC

gate-16 named two changed methods with no @SPEC. Tagging all five so the
class does not drift back over the line as it changes.
)

* feat(organisation): carry the four chain-partner fields it lacked

Version1Date20260831020000 made an organisation able to say it is a tenant
somewhere ELSE. That settled WHERE a counterparty lives, not WHO it is.

dossiq carries that separately in its own `partnerOrganization` schema, and
folding partners into Organisation would drop whatever this table cannot hold.

Measured against the live table rather than assumed, which corrected me: of
that schema's nine properties, `name`, `slug`, `isActive` and `groupId` already
map onto columns here, and `oin` ALREADY EXISTS alongside `tooi`. I had told
the team OIN was the missing field that decided the question. It was not. Four
properties genuinely had nowhere to go, and those four are what this adds:
contactEmail, defaultPermissionLevel, qualityScore, qualityStatus.

All four are nullable with NO default. A tenant of this installation has no
chain-partner contact address and has never been scored, and a zero score reads
as "scored badly" — a different claim from "never assessed", and one a partner
dashboard cannot tell apart afterwards. There is a test for exactly that.

🔴 `defaultPermissionLevel` is a DEFAULT, never an authorization input. ADR-002
Rule 1 keeps the organisation UUID as the only tenant key; nothing may read
this column to decide whether an actor may act, only what a NEW share is
proposed at. That is the trap `type` was deliberately kept out of, and the
docblock says so where someone would otherwise reach for it.

The field types register under the PROPERTY name, not the column name: Entity
resolves a setter to lcfirst(substr($method, 3)) and looks that up, so a
snake_case registration matches nothing and the integer cast silently never
runs. Tested.

Note for reviewers: OrganisationTest reports 3 PHP warnings from inside
Nextcloud's own Entity.php. They are pre-existing — 3 before this change and 3
after, with 67 tests becoming 70.

* fix(quality): restore the copyright tag and group the suppression

Two CI findings, both mine.

gate-1 spdx-headers: the new migration carried @license but no @copyright.

phpcs: the PHPMD.LongVariable suppression sat between the @method tags and the
rest, separated by a blank line, which breaks "tags must be grouped together".
It now joins the existing class-level suppressions, and the REASON it exists
moves to the property's own docblock where it is actually read: Entity maps a
property to its column BY NAME, so `defaultPermissionLevel` must be the
camelCase of `default_permission_level`, and shortening it to satisfy the rule
would stop the mapping silently rather than failing.

A bare suppression tag with the reason deleted would have satisfied phpcs and
told the next reader nothing.
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ 622f55a

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

Quality workflow — 2026-08-31 21:53 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ 8efdb3e

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

Quality workflow — 2026-08-31 22:15 UTC

Download the full PDF report from the workflow artifacts.

…the instance (#3249)

New change in the ADR-098 chain, depending on flow-task-entity and
flow-user-task-node. A separate openregister.portal-task node (not an
external mode of user-task: performer resolution, delivery channel and
completion payload all differ, and a mode would make half of each node's
config keys invalid per mode) that:

- matches the party from the case object's initiator role, frozen at
  creation and audited
- creates a task with the external performer type (ADR-098 D3 amendment
  2026-08-31), delta on the flow-tasks capability
- suspends heartbeat-safe with a non-null resumeAt (findAbandonedSignals
  matches resume_at IS NULL; the 14-day reaper must never see a
  hersteltermijn)
- delivers through portaliq's contribution surface (ADR-046); rendering
  is portaliq's follow-up change, one task line only
- accepts an upload completion stored as an OR file attachment on the
  case object (ADR-022, decision 2026-08-31)
- refuses completion by anyone but the matched party, fail-closed
- loops on re-ask with a mandatory reason
- consumes flow-business-timers for reminder, escalation and expiry
The fleet is dropping n8n: OpenRegister flows become the only engine. The
descriptor is the first piece to go and the only one this schema audit
touches.

It declared five schemas with no `slug` key at all, so their slugs derived
from their titles, and two of those, `notification` and `schedule`, collided
with decidiq and hermiq in the fleet-wide scan. It reports `absent` on the
dev instance, so it shipped and was never imported: this clears a static
collision, not a live one.

testEveryDescriptorOpenRegisterShipsIsDiscovered counts declaring files by
reading lib/Settings itself, so it follows the deletion rather than
breaking. The comments that named this file as a live example are rewritten
to name one that still exists: a comment asserting a file ships is a claim,
and a stale one is worse than none.

The server-side retirement (N8nSettingsController, WorkflowEngineController,
WorkflowEngineRegistry and the n8n branches in FlowEngine) is its own change.
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ 9056ad3

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

Quality workflow — 2026-09-01 04:58 UTC

Download the full PDF report from the workflow artifacts.

)

New change, no dependencies beyond shipped code. Three requirements:

- GET /api/flow-runs/active accepts an optional subject filter matching
  FlowRun.subject_uuid, applied in the datastore inside the caller's
  organisation scope; the filter can only narrow, and the total counts
  the filtered set
- a completed-runs read for a required subject (FlowRun::TERMINAL,
  newest first, bounded, honest total) so a case page can show history
  without widening the flowRun#index surface, which or-flow-active-runs
  requires to stay unchanged
- one row contract for both reads: run uuid, flow name, current step,
  status, started at and the subject block; never marking, items or the
  step log

The nc-vue consumption (subject option + run deep link on
CnFlowRunsWidget) is one follow-up task line pointing at an nc-vue
change.
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ eff4892

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

Quality workflow — 2026-09-01 05:02 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ e6f2d24

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

Quality workflow — 2026-09-01 05:24 UTC

Download the full PDF report from the workflow artifacts.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ 8e1b46b

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

Quality workflow — 2026-09-01 06:00 UTC

Download the full PDF report from the workflow artifacts.

rubenvdlinde and others added 2 commits September 1, 2026 08:56
…3260)

The live-runs read (GET /api/flow-runs/active) accepts an optional
subject parameter (a subject object uuid). It narrows INSIDE the
caller's organisation scope in the datastore: the organisation predicate
stays unconditional and the subject is a second AND predicate on
subject_uuid, so a guessed uuid from another tenant matches nothing. The
total counts the filtered set. Without subject the read is bit-identical
to before.

A completed-runs read (GET /api/flow-runs/completed) answers the history
half for the same subject: FlowRun::TERMINAL statuses, subject REQUIRED
(400 naming the parameter, never an org-wide history dump), same
organisation scope, newest first, capped limit, honest total. It reuses
summarise(), so both reads share one row contract: uuid, flow name,
current step (null when no marking), status, created, subject block, and
never the marking, items or step log. flowRun#index is untouched.

One composite index on (organisation, subject_uuid, status, id) makes
both reads a range scan on one case rather than a walk over the tenant's
runs.

Tests: controller (subject reaches rows and total, no org means no
query, blank subject is no filter, completed read refuses a missing or
blank subject, capped limit, shared row shape without heavy fields) and
mapper (predicate order with the organisation first, terminal set
including failed, newest-first bound, fail-closed on a missing
predicate).

openspec: flow-runs-subject-scope, tasks 1.1, 1.2, 2.1, 2.2, 3.1, 5.1, 5.2

Co-authored-by: Ruben van der Linde <juan.claude@conduction.nl>
… through the notification subsystem (#3257)

* feat(flow): send-notification, send-email and send-talk-message nodes through the notification subsystem

Three messaging step nodes, implemented as orchestration-time invokers of
the ADR-031 channel machinery rather than a second messaging stack:

- The per-channel send units (nc-notification + web-push ride-along, email
  composition/handoff, Talk post), the recipient resolver and the dialect's
  placeholder evaluator are extracted from AnnotationNotificationDispatcher
  into call-shared units under lib/Service/Notification/; the dispatcher is
  re-wired onto them with its existing tests green and unchanged.
- FlowMessagingService applies the guards in order: per-channel subsystem
  kill switches (new, default-on, silencing BOTH callers), the recipient's
  own channel preference, a post-expansion recipient bound (app-config
  raisable), and the RateLimiter — whose buckets gain a caller-agnostic
  shared per-recipient budget so a flow cannot double a person's ceiling.
- Sends are attributed to the run's acting user; no resolvable actor fails
  the step naming the missing actor. Talk posts as the acting user and
  treats "not a participant" as a step failure, never an auto-join.
- Per-recipient/channel outcomes land on the run log through a new
  FlowStepReport context handle the engine drains per hop, bounded by the
  log's sampling rule; a send failure is a step failure through onError.
- No send-webhook node: outbound HTTP stays with OpenConnector (ADR-094).

@SPEC openspec/changes/flow-messaging-nodes/specs/flow-messaging-nodes/spec.md

* chore(flow): satisfy the spec-coverage and spec-anchor gates on the messaging nodes

The config-form floor is cited at its shipped home in the flow-engine spec
(the flow-node-config-forms change carries no spec file under that name),
and the resolver's shared helpers and the Talk app boundary carry the
messaging spec anchors they implement.

---------

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

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ 9e9a57d

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

Quality workflow — 2026-09-01 07:01 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ 12f175a

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

Quality workflow — 2026-09-01 07:21 UTC

Download the full PDF report from the workflow artifacts.

…3263)

2.28.0 carries the canvas run animation (nextcloud-vue#896) and the
subject-scoped runs widget (nextcloud-vue#898), the two surfaces the
flow-runs-subject-scope read (#3260) exists for.

The bump could not be installed at all: #3232 rewrote the overrides as
$-references and `npm install` refuses `$postcss` with "Unable to
resolve reference". The other references resolve; this one does not,
so it is the one direct spec again, matching the devDependency.

Co-authored-by: Ruben van der Linde <juan.claude@conduction.nl>
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ 4388348

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

Quality workflow — 2026-09-01 08:12 UTC

Download the full PDF report from the workflow artifacts.

rubenvdlinde and others added 2 commits September 1, 2026 11:54
Every object sub-resource endpoint answered 404 "Object not found", in every
app on the instance: notes, tags, relations, emails, polls, talk, bookmarks,
deck, calendar, tasks, time-tracker and the rest. The object itself resolved
fine at /objects/{register}/{schema}/{id}, and the identical resolution chain
succeeded from occ, which is what made this hard to see.

Under HTTP it threw `Schema slug "case" is not carried by register "buildiq"`
on a request that named dossiq. ObjectService::setSchema() scopes its slug
lookup to whatever register is CURRENTLY set, and the service is reused across
many operations in one process, so setting the schema first resolves the slug
against a register left behind by an unrelated earlier call. Buildiq happens to
own a `case` schema too; the global lookup reached it first.

setRegister() already re-resolves a pending schema ref for exactly this reason,
and its own comment names this helper as the shape that inverted the two. That
repair cannot help here, because the throw happens inside setSchema() before
setRegister() is ever reached.

25 controllers carried the inverted order. FilesController did not, and its
docblock already described this bug, so the rule was known and simply never
applied to the others.

Worth naming what this cost: every one of those surfaces renders an EMPTY
STATE, not an error. A case with notes said "No notes yet". The page did not
report that it could not ask, so the failure was indistinguishable from the
data genuinely being absent.

The regression guard is a static scan over every controller rather than a
behavioural test. Reproducing the runtime symptom needs a second register that
happens to share a slug AND a prior operation that left its register set, which
is a state no unit test would reach naturally and one a live instance reaches
constantly. Verified the test fails when the order is put back.

Verified on the dev instance: notes, tags, relations, emails, files and
audit-trails all return 200 where they returned 404, including pipelinq, an app
untouched by this change. 3227 controller tests pass.
`npm run lint` was `eslint src`, so two whole trees were never linted. Across
the fleet that hid roughly 3,900 errors, none of which any CI leg had shown.

Most of it was the config, not the code, and the same two defects were in
every app.

**scripts/ had no config block at all.** These are CommonJS Node CLI checkers,
and flat config defaults every `.js` to ESM with browser-ish globals, so eslint
read the CommonJS wrapper itself as undefined identifiers: `require`,
`process`, `__dirname`, `__filename`, `module`. A `scripts/**` block now
declares the environment. Declaring beats suppressing here: `no-undef` is the
rule that catches a genuinely misspelled identifier, and dozens of fake
findings would bury a real one. A second block covers `scripts/**/*.mjs`, which
is ESM and needs Node's globals without the CommonJS wrapper.

**The tests block applied a non-TypeScript-aware rule to TypeScript.** It named
`tests/**/*.ts` while setting the CORE `no-unused-vars`, which v9 deliberately
turns off for `.ts` in favour of the `@typescript-eslint` version. The core
rule reads the parameter names inside a function TYPE as bindings, so

    t?: (app: string, key: string) => string

reports `app` and `key` as unused variables, and every unused `catch (e)` in a
`.ts` spec reports twice. The block is split now: `.js`/`.mjs` on the core
rule, `.ts`/`.tsx` on the TypeScript one, same patterns on both.

Also: stale `eslint-disable` comments naming plugins eslint 10 no longer
registers, which are themselves errors ("Definition for rule ... was not
found"), and a rule that must not parse shell scripts.

The genuinely real findings were the useful part: dead locals, unused imports,
dead helper functions, unused `catch` bindings, extensionless relative imports,
and a handful of `== null` comparisons spelled out so they still match null AND
undefined.

Verified per app: `npm run lint` 0 errors over src + tests + scripts,
`prettier --check` clean, and the unit suite still green.
? [`pm.test('${c.key}: ${c.expect.status} rows are gone', () => pm.expect(matching.length).to.eql(${c.expect.exactly}))`]
: [`pm.test('${c.key}: wrote at least ${c.expect.atLeast} ${c.expect.status} object(s)', () => pm.expect(matching.length, 'the run was green but wrote nothing').to.be.at.least(${c.expect.atLeast}))`]),
]
`const matching = objects.filter((o) => o.status === ${JSON.stringify(c.expect.status)})`,
` pm.expect(other.length, 'only one branch produced anything — nothing was actually split').to.be.at.least(${c.expect.also.atLeast})`,
'})',
]
`const other = objects.filter((o) => o.status === ${JSON.stringify(c.expect.also.status)})`,
` pm.expect(Number(carrying[0][${JSON.stringify(c.expect.field.name)}])).to.eql(${c.expect.field.equals})`,
'})',
]
`const carrying = objects.filter((o) => o.status === ${JSON.stringify(c.expect.status)})`,
` pm.expect(String(bearing[0][${JSON.stringify(c.expect.text.name)}])).to.eql(${JSON.stringify(c.expect.text.equals)})`,
'})',
]
`const bearing = objects.filter((o) => o.status === ${JSON.stringify(c.expect.status)})`,
` pm.expect(String(named.status)).to.eql(${JSON.stringify(c.expect.step.status)})`,
'})',
]
`const named = steps.filter((s) => s.transition === ${JSON.stringify(c.expect.step.node)})[0]`,
` pm.expect(batchStep.itemsOut, 'batching did not reduce the item count').to.be.at.most(${c.expect.batched.maxOut})`,
'})',
]
`const batchStep = steps.filter((s) => s.transition === ${JSON.stringify(c.expect.batched.node)})[0]`,
…e-schema

fix(controllers): name the register before the schema
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/openregister @ 54d49a3

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

Quality workflow — 2026-09-01 10:30 UTC

Download the full PDF report from the workflow artifacts.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants