Skip to content

Release: merge development into beta - #563

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

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

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

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

Merging this PR will trigger the beta release workflow.

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

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

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

Per-widget `valueColor` is applied as an INLINE style on the number, so it beats
the canonical card accent, and a hex literal opts the card out of NL Design
System theming — which kpi-card.css forbids outright, because the nldesign app
re-themes by overriding the Nextcloud tokens.

Two outcomes, no third:

  * a colour that merely restated the default accent is deleted — `#0082c9` IS
    `--color-primary-element` in the default theme, so the tile looks identical
    and now re-themes correctly;
  * a colour that carried meaning becomes `variant`, which is themed and drives
    the icon tint and the number together so the two cannot disagree.

`variant` on a stat/delta resolves through VARIANT_COLORS, which was moved onto
the `-text` tokens first (nextcloud-vue#888) — the plain fill tokens failed WCAG
AA at 1.08:1 as a foreground colour, and these conversions would have been the
first tiles to hit that path.

Edited as text rather than re-serialised, so the manifest keeps its hand-laid-out
formatting; every `valueColor` in this file was confirmed to sit on a KPI widget
first. Manifest schema validation passes.
Dependabot cannot propose composer updates for this app. Its updater fails
with:

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

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

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

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

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
…2.6-beta.20260831102714

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/keepiq @ 1292479

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:46 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/keepiq @ 1042c3a

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

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

Download the full PDF report from the workflow artifacts.

…ection (#479)

* feat(vault): secret details in a right sidebar over the list

Clicking a row now opens the secret in an NcAppSidebar over the vault
list instead of navigating to a separate page, so the list context
(folder, search, pagination) stays visible and intact behind it.

The detail page is gone: the id rides as an optional segment on the
list routes (/secrets/:id? and /folders/:folderId/:id?), because
CnPageRenderer keys its render on the page id and a separate page
remounted the list on every open. Deep links to #/secrets/<id> keep
resolving (list + open sidebar) and stay behind the vault-lock guard.
Route <-> sidebar mapping lives in src/utils/detailRoute.js.

The sidebar lays the detail out Proton Pass style: the action row
(labeled Edit opening the edit dialog, icon-only Share with ariaLabel,
and a "Secret actions" menu holding Move, Delete and Close - the
native X is hidden while the row renders; Esc still closes everywhere)
sits with the title in the header; fields render as grouped
icon/label/value boxes with a visible attachments box and a
last-modified/created metadata box (NcDateTime); the informational
tail folds into "More information" (sharing, requests, activity) and
owner-only "Advanced" (versions, rotation, honey) disclosures. Card
and identity secrets render their payloads as first-class rows (masked
number/CVV/PIN/BSN with eye + copy, revealed numbers regrouped in
blocks of four, identity split into Personal/Address/Contact
sections); the old CardDisplay/IdentityDisplay components are deleted
and the raw key row is hidden for composite types. The pane widens to
clamp(300px, 35vw, 560px) above the library's mobile breakpoint.
Decryption semantics are unchanged. Edit/Move refresh the list behind
the sidebar; Delete closes it.

The SecretDetail vitest specs are retargeted at the sidebar component;
the new route mapping has its own spec; e2e drives Move/Share through
the new action row and opens the More-information disclosure for the
activity assertion; the guards spec drops the removed page id.

* l10n: detail-sidebar strings in every required locale

New source strings from the sidebar restyle - "Details", "Versions",
"Secret actions", "More information", and the identity section
headings "Personal details" / "Address details" / "Contact details" -
translated into English plus all 36 required locales, with the .js
catalogs regenerated and the parity ratchet tightened to record the
progress.

* fix(nav): highlight only the "..." node when its hidden folder is open

Opening the folder the "..." stand-in node represents lit TWO rows in
the rail: the node itself (vue-router exact-active styling on its
link) and its parent, which received the deepest-visible-ancestor
highlight. The ancestor rule now returns nothing when the active
folder is the single hidden child the "..." stands for, so exactly one
row reads as selected. Navigation through the node is unchanged, and
the multi-hidden-children case keeps highlighting the parent row as
before.

* feat(vault): show a loading state while switching folders

Navigating to another folder used to keep the previous folder's rows
on screen until the new ones landed, then swap them mid-air -
CnIndexPage only shows its full loading spinner while it is loading
AND has nothing to render. A folderSwitching flag now blanks the list
for the duration of the navigation's fetch, so a switch reads as
spinner -> new folder's contents. The flag starts true because
root <-> folder navigations REMOUNT the view (the routes are different
manifest pages and CnPageRenderer keys its render on the page id), and
a fresh mount rendered the store's previous rows before mounted()
fetched anything - the folderId watcher only covers folder -> folder
moves. In-folder refreshes and searches keep the in-place behavior.

* l10n: translate the English-copied strings across all locales

A sweep over every locale for values identical to their English source
found six bulk-copied strings: "Applications awaiting approval", "No
applications awaiting approval" (31 locales), "Your secrets, folders
and applications at a glance", "Vault overview", "Rotation due" (33
each) and "View all" (31). All now carry real translations phrased
with each locale's established terminology. Script-mismatched
loanwords are also fixed: Token and Status become Cyrillic in mk/sr,
and Link becomes Länk/Ligação/Colliaziun in sv/pt/rm. Values that
already held a genuine translation were never touched, and legitimate
loanwords (URL, Token in Latin-script locales, Dashboard, Widgets,
Layout) and the brand string "Keepiq {version}" stay as they are. The
.js catalogs are regenerated.

* fix(l10n): translate the secret-type labels everywhere they render

The secret types come from the server with English labels seeded by
SeedSecretTypes.php, and the frontend printed type.label raw - so the
type filter, the create and edit dialogs' type selectors and the
detail sidebar's type subline showed English in every language. A new
secretTypeLabel() helper (src/utils/secretTypes.js) routes the label
through the translator at all four sites, falling through unchanged
for custom types without a catalog entry - the dynamic-key pattern the
nav already uses for manifest menu labels. The ten system labels are
translated in English plus all 36 required locales (295 new entries;
seven keys were missing from every catalog), the .js catalogs are
regenerated and the parity ratchet is tightened accordingly.

* fix(security): remove the dev auto-unlock block that slipped into the l10n commit

Commit 17988a7 accidentally carried a local development aid into
LockScreen.vue: a DEV-ONLY auto-unlock block that reads the master
password in plaintext from localStorage and unlocks the vault on page
load, plus its status banner and data field. It was guarded by a
NODE_ENV check (webpack strips it from production builds) and inert
without an explicit localStorage opt-in, but it defeats the lock
screen's security model and was never meant to be tracked.

The same commit also dropped two @SPEC tags from handlePasskeyUnlock;
they are restored. The file now matches its pre-17988a77 state exactly.

* feat(vault): fleet selection with a contextual strip, vault strip, top-row toolbar

The vault list adopts CnIndexPage's own selection: selectable with
rowClickToView (clicking opens the detail, checkboxes select),
selectedIds fed from the bulk store and @select synced back. The table
gets header select-all, row checkboxes and selected-row highlight;
cards get a checkbox each; the list keeps shift-range selection through
NcCheckboxRadioSwitch — the same Nextcloud checkbox the other views
render — with a capture-phase shift recorder. One selection is shared
across view switches, pruned to the visible rows on every list change
(folder navigation, page flips, filters, refresh), and the strip's bulk
buttons disable while the list is (re)loading so a stale selection from
the previous page cannot be moved or deleted.

Bulk actions live in the library's contextual selection strip alone
(live count announced via role="status" — WCAG 2.1 SC 4.1.3 — plus
Move, Share, Add to team folder and Delete with icons, and the strip's
own Clear; bulk-actions §3.1's required bar). The Actions menu keeps
only the Select-all checkbox — the sole whole-view select-all for the
list and card views. Selected list rows tint the item with its hover
token, never the checkbox gutter.

Folders left the collection: vaults/subfolders render as a captioned
strip (Vaults at root / Folders inside) above the collection in every
view mode (the library's #before-collection slot), so they no longer
masquerade as secrets in table/cards and survive pagination; the strip
owns the single divider. Breadcrumbs render below the actions bar,
above that strip, a step larger — the trail is the folder heading now
that the page title is visually hidden (the h1 stays for
accessibility).

The bar owns the top row: keepiq's doubled page padding is dropped and
the bar clears the floating nav toggle with a 30px !important margin —
a margin so its background starts after the toggle, !important because
design-system themes flatten the bar's box with their own !important
rules. Keepiq-scoped, as is the container-scale rounding; other apps
keep their heading and the library defaults. The secret-type filter is
a funnel button beside the search field carrying the sort radios too;
it flips to its filled glyph in the primary color whenever a type
filter is set or the sort deviates from the default name sort. New
l10n string "Filter and sort" in en + all 36 required locales.

e2e: clickOverflowAction opens the bar menu via a new openActionsMenu
helper; export-gdpr's four inline menu openers use it.

* fix(nav): the "…" node carries the selection for any folder below the display cap

Being in a folder deeper than the rail renders (e.g. test11 under the
5-level cap) highlighted the deepest visible ancestor (test5), which
read as the wrong folder being open. The active trail is computed once
(activeFolderTrail); when it exceeds the cap no ancestor row lights up
and the "…" node under the deepest visible branch renders active
instead (ellipsisHighlightId, passed into NavFolderTree's ellipsis
item) — exactly one row selected, and it is the row that stands for
the hidden chain. This subsumes the earlier single-hidden-child
special case.

* fix(icons): draw the lock shackle as a filled outline in every app icon

ImageMagick's internal SVG renderer — used by Nextcloud theming to
generate the favicon — does not draw stroked paths, so the shackle
vanished from generated favicons. All five icons (app, app-dark,
app-store, pwa-icon, pwa-icon-maskable) redraw the shackle as a filled
outline of equivalent thickness, so stroke-less rasterizers render the
full lock.

* style(vault): no blank line after the crumbs custom property

CI's stylelint (declaration-empty-line-before) rejects a blank line
between a custom property and the following declaration — a formatting
habit that is valid in nextcloud-vue's stylelint config but not in
keepiq's. The full stylelint sweep is green again, and RESTYLE-PLAN's
per-stage verification list now names stylelint so the gate stops
being skippable locally.

* fix(quality): satisfy the four failing hydra gates

gate-16: @SPEC tags on the seven changed methods the gate named —
onRowCheck/onSelectAll (bulk-actions multi-select requirement), the
selectedFolderId watcher (folder management), typeFilterOptions and
both secret dialogs' typeOptions (secret types), and the detail
sidebar's mounted() (read secret).

gate-40: the list checkbox's ariaLabel binding becomes aria-label —
identical Vue behaviour, and the form-label rule recognises the kebab
form.

gate-45: the sidebar's accordion-chevron transition gains the
prefers-reduced-motion fallback the rest of the file already has.

gate-46: five @SPEC tags pointed at anchors that never existed —
requirement-secret-sharing is requirement-share-a-secret
(user-sharing) and requirement-cached-vault-is-read-only is
requirement-offline-mode-is-strictly-read-only (offline cache).

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

Brings the released library features the vault page consumes, until now
only available through a locally built dist overlaid into node_modules:
the contextual selection strip (live role="status" count,
#selection-actions slot, clear-selection wiring), the #after-search and
#before-collection CnIndexPage slots, the role-grouped actions-bar
layout with its narrow-width container-query tiers, clickToView on
CnCardGrid/CnObjectCard, and the CnPageRenderer fix that stops lifted
manifest fields (title, icon) falling through as HTML attributes on
custom pages — the "Vault" tooltip that hovered over every custom page.

With the package live, the overlay workflow is retired for this line of
work: node_modules now matches the lockfile again.

* style(sidebar): overflow-wrap instead of the deprecated word-break keyword

CI's stylelint (declaration-property-value-keyword-no-deprecated, in
the newer toolchain the merged lockfile installs) rejects
`word-break: break-word` — a deprecated keyword that always aliased
overflow-wrap behaviour. The detail rows' value wrapping now uses
`overflow-wrap: anywhere`, which wraps long secret values identically.
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/keepiq @ de9a0ea

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

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

Download the full PDF report from the workflow artifacts.

rubenvdlinde and others added 2 commits August 31, 2026 15:58
…#571)

The folder-sharing spec drove the new sidebar's "Secret actions" menu via
getByTestId('secret-detail-move'), but data-testid falls through to
NcActionButton's ROOT element, which is the <li> wrapper rather than the
<button> that carries the click handler. Dispatching a native click on the
<li> is a silent no-op, so SecretMoveDialog never opened and the spec failed
on the downstream '.move-form' assertion instead of at the click.

The testid resolving fine is what made this read as a dialog bug: the click
step passed and only the next assertion timed out.

Descend to the button so the handler actually fires. The sibling testids in
this spec (secret-detail-share, secret-detail-more-info) sit on NcButton and
a native <summary>, whose roots ARE the interactive element, so they are
unaffected.
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/keepiq @ 15871e4

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

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

Download the full PDF report from the workflow artifacts.

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Hold this until #573 lands on development, or it carries a vault bug to beta on its own.

#479 introduced the secret detail sidebar, which deliberately does not remount when you select another secret. PasswordField caches the decrypted plaintext for its component lifetime, so the cache now outlives the secret it belongs to:

  • edit the open secret and the field keeps showing the old plaintext
  • click another row while revealed and the panel shows secret B's name with secret A's plaintext, and Copy resolves A's value

Neither beta nor main has this today. SecretDetailSidebar.vue is 404 on both, and #479's commit is 6 commits ahead of beta, so the bug has never left development. That is why the ordering matters: promoting development to beta right now moves the bug without its fix.

#573 fixes it and also repairs the Move menuitem click that broke in the same restyle. Once it is on development this promotion is safe again.

#573)

PasswordField decrypts lazily and then caches the plaintext for its own
lifetime: toggle() only resolves while `plain` is still null, and
`revealed` persists too. That was harmless while the detail was a full
page that remounted per secret.

SecretDetailSidebar deliberately does NOT remount. Its own secretId
watcher says so: clicking another row "swaps the :id segment without
remounting this component". So the cache outlived the secret it belonged
to, in two ways that both matter for a password manager:

  - Edit the open secret: load() refreshes the secret, the field kept
    showing the OLD plaintext. This is what the e2e caught, asserting
    the edited value and receiving the previous one.
  - Click another row while revealed: the panel showed secret B's name
    with secret A's plaintext, and Copy resolved A's value.

The watcher already reset cardRevealed and bsnRevealed for exactly this
reason. It could not reach inside PasswordField, so the password row was
the one field that kept its cleartext across the swap.

Binding :key to a token bumped on every successful load() remounts the
field, which resets `plain` and `revealed` together. A changed secret is
re-masked until the user asks for it again.

Also: click the Move menuitem by role instead of dispatching el.click()
at a node guessed from NcActionButton's markup. The old approach broke
silently after the restyle. The trace shows the testid resolving in 0.1s
and .move-form then timing out for 10s, with the menu still expanded and
menuitem "Move" present in the snapshot: a click landing on the wrong
node is indistinguishable from a dialog that refuses to open. The
accessibility tree exposes the item whatever element renders it.

Fixes two e2e failures on development introduced by the Stage-8 restyle,
and two pre-existing lint errors in the same spec file.

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

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/keepiq @ a3a5c33

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

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

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/keepiq @ 1e1223d

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

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

Download the full PDF report from the workflow artifacts.

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Clear to promote. #573 is on development and verified.

The post-merge push run is the one that counts here, because Playwright is skipped on pull requests into development, which is how the bug reached development unnoticed in the first place. Run 33404314404 on 759edc1: E2E success, 70 passed, 0 failed. Before the fix the same suite was 2 failed and 68 passed.

So this promotion now carries the restyle and its fix together, which was the whole point of holding it.

rubenvdlinde and others added 2 commits August 31, 2026 18:15
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>
#576)

SecretRequestFill "encrypts on submit and shows the success message"
failed once in roughly twenty full-suite runs with

  AssertionError: expected "Mock" to be called at least once

and passed 12 out of 12 times when the file was run on its own. That
split is the tell: the failure needs a loaded suite, not a broken
component.

The helper these specs share is

  const flush = () => new Promise((resolve) => setTimeout(resolve, 0))

which yields exactly ONE macrotask tick. The submit path awaits an async
encryption chain before it posts, and one tick is not guaranteed to drain
it. Under parallel workers competing for CPU the assertion can run before
the call lands, so the test reports a component that never posted when in
fact it had not posted YET.

Five sites share that shape: `await flush()` followed immediately by
`expect(<mock>).toHaveBeenCalled()`. All five now wait for the call:

  await vi.waitFor(() => expect(post).toHaveBeenCalled())

This is strictly stronger than the fixed tick. It retries until the call
arrives and still fails when it never does, which was verified rather
than assumed: removing the submit trigger from the component makes the
test fail (exit 1). The first attempt at that control was itself useless
and is worth recording, because it looked like it worked. Renaming the
event to `submit.prevent-DISABLED` still fires a submit, since Vue Test
Utils reads everything after the dot as modifiers, so the sabotage was
inert and the suite passed. A negative control that does not actually
break the thing proves nothing.

flush() is left in place for the many uses that only need a render tick.

Also fixes 15 pre-existing import-order errors in the same five files.
They are not gating: `npm run lint` covers `src` only.

Verified: unit suite 678/678 on four full runs, eslint clean on the
changed files, prettier clean.

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

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/keepiq @ 2b665b7

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

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

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/keepiq @ 389b6b0

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

Quality workflow — 2026-08-31 16:48 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