Repository navigation
Release: merge development into beta - #1866
Merged
Merged
Conversation
…0260906124625 chore(sync): carry beta back into development
… dynamically
Nine call sites in the rule editor pass a VARIABLE to t(), so no static
scan can see them:
ACTION_TYPES / TIMING_OPTIONS / ACTION_OPTIONS (views/Rule/ruleDraft.js)
-> t('integriq', entry.label) in RuleActionConfig.vue,
RuleDetailPage.vue and modals/v2/RuleEditorModal.vue
OPERATORS (views/Rule/RuleConditionLeaf.vue)
-> t('integriq', op.label) and t('integriq', op.group)
the field rows in views/Rule/actionForms/{Approval,Authentication,
Locking,WebhookSignature}Form.vue -> t('integriq', row.label)
DYNAMIC_KEYS exists for exactly this and was empty, on the stated grounds
that integriq reaches all its dynamic UI copy through src/manifest.json.
It does not, and the cost was two-sided across 69 strings:
- 9 were in en.js, reported UNUSED, and offered up by clean:l10n for
deletion. Deleting a live translation leaves the English source
rendering correctly, so nothing would have failed.
- 60 were not in en.js at all, which is why the entire rule editor
renders untranslated in every locale: action types, condition
operators ("does not equal", "greater than"), authentication methods,
webhook signature schemes.
The 60 are added and all 69 are now claimed. Adding 60 keys did NOT raise
the unused count (670 -> 668), which is the control: without the claim it
would have gone to 730.
CLAUDE.md was wrong about the catalogues and that is what made this
awkward. l10n/*.js is GENERATED from l10n/*.json by build-l10n-js.js, and
check:l10n-js fails on drift, so they are not "separate catalogues, not
two renderings of one source". The practical consequence is that
scripts/l10n-ai.js is the wrong tool for adding a key: its help says it
never touches the JSON, so its writes are discarded by the next
l10n:build, silently. I added the 60 with it first and only noticed when
check:l10n-js reported a stale browser catalogue. The keys now go into
en.json and l10n:build regenerates en.js.
Verified: check:l10n MISSING 0, UNUSED 670 -> 668; test:l10n exit 0;
check:l10n-js exit 0; clean:l10n no longer offers "Save object",
"Approval", "does not equal" or "Lock resource"; all 77 applicable hydra
gates green with the runner CI resolves, and all 77 ran.
Dutch values are deliberately absent: en is required, other locales are
optional, and an absent value falls back to the English source while
staying visible on the to-translate list. A value equal to its key would
not.
fix(l10n): claim the rule editor's option lists, which are translated dynamically
rubenvdlinde
requested review from
Rem-Dam,
SudoThijn,
WilcoLouwerse,
bbrands02,
remko48,
rjzondervan and
rubenvdlinde
as code owners
September 7, 2026 07:36
CLAUDE.md said there was "no scanner for the backend set" and that auditing it "would mean walking lib/ for PHP $l->t() calls". This is that walk. It matters because the two catalogues are not independent: l10n/<locale>.js is GENERATED from l10n/<locale>.json, and the JSON is what PHP IL10N reads. A string only the backend translates therefore appears in en.js legitimately, and the frontend scan calling it "unused" is a false positive rather than a finding. Measured 2026-09-07: 409 distinct strings reach PHP ->t() in lib/, and 71 of them were reported as UNUSED frontend keys and offered up by clean:l10n for deletion. Among them "Account is disabled", "Commit contracts" and "Apply a mapping". Deleting one removes the backend's translation and leaves the English source rendering correctly, so nothing would have failed and nobody would have noticed. check:l10n and clean-l10n now union collectBackendKeys() into the set they already build from collectDynamicKeys(), which is the same idea for the frontend: keys no scan of src/ can see. The matcher is deliberately generous, treating any object's ->t() with a literal first argument as a translation. A false positive here only keeps a key alive, which is the safe direction; a false negative deletes a live translation. Verified: UNUSED 668 -> 596, MISSING still 0, test:l10n exit 0, clean:l10n no longer proposes the three named above, eslint and prettier clean. What is left is a real number for the first time: 596 keys that neither the frontend nor the backend references.
feat(l10n): scan the backend catalogue, which nothing was auditing
`<p v-if="schemasTemplateLoading">Loading...</p>` rendered raw, so it said Loading in every locale. It is the one live-prose hit from cross-checking find:unwrapped against the unused-key set, which is the check the app's own notes ask for before removing anything. Uses the ellipsis character, which @nextcloud/l10n-enforce-ellipsis requires and which is already a key. Wrapping the triple-dot spelling tripped the rule and, because the file carries an eslint suppression count, surfaced a second pre-existing violation at line 1579 along with it: the suppression absorbs a fixed number, so one new error reveals the others. 'Loading...' with three dots is now correctly dead.
…ying check:l10n reported 674 unused keys when this work started and now reports none. The count was never the problem; the fact that it could not be trusted was. Three classes of false positive were removed first, each in its own commit, because deleting before that would have taken live translations with it: - the manifest strings collectDynamicKeys did not claim - the rule editor's option lists, reached through a variable - the 409 strings PHP ->t() translates, which nothing audited Then the documented cross-check: find:unwrapped against what remained. It found exactly one live string, "Loading...", which is now wrapped and uses the ellipsis character the lint rule wants. The remaining 595 are referenced by neither the frontend nor the backend. They are removed from all 37 locale catalogues, 19,594 entries, and the .js files are regenerated from the .json, which is the source. GATE-102 CAUGHT EIGHT I WOULD HAVE LOST. Walkthrough copy, four column labels and a header action are manifest strings in fields collectDynamicKeys did not harvest: it took `pages[].title` and report card labels, not `body`, `task` or a column's `label`. The gate reads a wider set than the harvester did, and only the gate noticed. They are restored with Dutch values, and collectDynamicKeys now harvests the SAME visible-field set gate-102 checks, so the two cannot disagree again. Field-scoped rather than every string: `observability.metrics[].name` holds Prometheus identifiers and stays out. Verified: check:l10n exit 0 and "all clean" for the first time; MISSING 0; test:l10n exit 0; check:l10n-js exit 0; all 79 applicable hydra gates green with the runner CI resolves, and all 79 ran. test:l10n:parity still fails, as it did before, and slightly less: 169 missing became 164, because there are fewer English keys to translate.
chore(l10n): remove 595 keys nothing references, and stop the audit lying
CI caught what I missed. The 595-key sweep removed 55 strings that a shipped register descriptor declares: schema titles, property titles and helper text, rendered by OpenRegister's form renderer. They reach the user through neither a t() call in src/ nor PHP ->t(), so both existing collectors missed them, and check:schema-l10n was the only thing that noticed - on CI, because it is not part of check:strict and I never ran it locally. It reported 1166 uncovered against a baseline of 1120. The 55 are restored, and collectSchemaKeys() now claims them so a future sweep cannot repeat this. The collector is deliberately the SAME walk as scripts/check-schema-l10n.js `collect()`, copied rather than reinvented: recurse everywhere, and at any node carrying `properties` harvest the schema title, each property's title and description, and x-enum-labels. My first attempt walked one level and missed seven nested titles (event_subscription.properties.action.properties.*, and a title under items.properties) plus every enum label. Both now report exactly 1215 schema strings, which is the check that they agree. That is the same lesson as gate-102 earlier in this work: when one tool decides coverage and another decides what a sweep may delete, they have to read the same set or the gap is where live text disappears. Baseline lowered 1120 -> 1111, which the tool offers and which locks in the nine strings that are now covered and were not before. Verified: check:l10n exit 0 and all clean; check:schema-l10n exit 0; test:l10n exit 0; check:l10n-js exit 0; eslint and prettier clean; all 77 applicable hydra gates green with the runner CI resolves, and all 77 ran.
fix(l10n): the register descriptors are a fourth source of live keys
…ding The connector-catalog specs were skipped wholesale in one describe, with an accurate reason and tracker #1187. Measured against a live instance rather than taken on trust, two of the four are reachable today and two are not, so the blanket skip was costing more coverage than it needed to. What the specs assumed, and what is actually there: getByRole('searchbox') 0 matches. The one visible input[type=search] is Nextcloud's own header search and does not filter. getByRole('button', 'Source the quick-filter chips are role="tab", templates') so the click never landed. This is why "the filter does not work". a non-paginated grid it pages at 20 of 57, so PDOK is on page 2 and toBeVisible() failed before any badge assertion ran. BRP shows "available" it is dormant. available and dormant are the only two status values across all 57 rows. So: - The quick-filter test is revived against role="tab", and asserts the "Showing N of M" TOTAL rather than the card count. The count cannot answer the question: it is pinned at the page size of 20 whether 57 or 35 items match, so the original assertion could not have failed. - The PDOK dormant test is revived behind a helper that walks pages until the card is on screen. The badge assertion re-resolves on every poll, because the grid re-renders as its page query settles and a locator captured once goes stale, which reads as "no badge" (it did, once). - BRP and the search test keep individual skips carrying what was measured, so the report says why rather than pointing at a describe. - REQ-002 is untouched: I did not probe it, so I have nothing to say about it that #1187 does not already say. Both revived tests were verified to FAIL when inverted (asserting the total grows, and asserting a status the schema cannot hold), so neither is a test that cannot fail. gate-19 goes 337 to 334 findings. All 77 applicable hydra gates green with the runner CI resolves, and all 77 ran; the spec file runs 5 passed, 4 skipped, exit 0. Full measurements are posted on #1187 for whoever takes the remaining four.
test(catalog): revive the two store specs that a describe.skip was hiding
…1840) * fix(sync): parse RFC 6266 Content-Disposition (filename*) — WOO-552 Port the RFC 6266 parser fix from the WOO stable hotfix (hotfix/woo-552-content-disposition-rfc6266, release `v0.2.24-woo-1`) to the development branch, so the next main / stable release does not re-introduce the bug. Same shape as the stable fix: - Replace the naive `explode('=', $header)` in `SynchronizationService::getFilenameFromHeaders()` with a call to a new private helper `parseContentDispositionFilename()` that: - splits the header on `;` (so `filename*=UTF-8''pct-encoded` no longer fools the extractor), - prefers `filename*` over `filename` per RFC 6266 §4.3, - accepts case-insensitive parameter names, - strips quotes on the plain `filename` value, - decodes the pct-encoded UTF-8 payload of `filename*` via `rawurldecode()` (RFC 3986 §2.1), - logs and falls back on non-UTF-8 charsets. - Add the second helper `decodeRfc5987ExtendedValue()`. - Add 10 unit tests via `SynchronizationServiceContentDispositionTest`, reaching the private parser through `ReflectionClass` — same tests that cover the stable fix. Method signature on development is `getFilenameFromHeaders(array $response, ObjectEntity $result)` (the URL-fallback branch reads the request via `$result->getObject()['request']`); only the Content-Disposition branch is touched by this commit, the URL-fallback logic is left as-is. Triggered by xxllnc's Zaken API change of 2026-08-19 (started emitting both `filename` and `filename*`, breaking OpenWoo.App document downloads; rolled back but scheduled to re-roll behind a feature toggle). Stable hotfix `v0.2.24-woo-1` is already live as prerelease for direct install on WOO production; this PR ensures the next main-line release does not regress. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(sync): address PR #1840 review — CI style + quoted-string tokenizer Addresses the 8 findings from the self-review on #1840: phpcs / phpmd (CI-red) — SynchronizationService.php: - Line 9056, 9128: use named arguments on internal helper calls. - Line 9120: rename `$eq` → `$eqPos` (phpmd ShortVariable, min 3). - Line 9171: capitalize inline comment ("Decode via rawurldecode()…"). Parser correctness (RFC 6266 §4 quoted-string grammar): - Replace `explode(';', $header)` with a quoted-string-aware tokenizer `splitHeaderParameters()` so a `;` inside `filename="…"` no longer corrupts the value. `filename="foo;bar.pdf"` now returns `foo;bar.pdf` (was `foo`). Test file (SynchronizationServiceContentDispositionTest.php): - Fix namespace: `OCA\OpenConnector\Tests\...` → `OCA\Integriq\Tests\...` to match composer autoload-dev psr-4 rule (was skipped by composer's autoloader with a warning; every other test in tests/Unit/Service/ uses the Integriq namespace). - Add 3 tests: - `testFilenameWithSemicolonInsideQuotedValuePreservesFilename` - `testFilenameWithPathTraversalPayloadIsReturnedVerbatim` (parser contract test — sanitization is downstream's responsibility) - `testFilenameWithWhitespaceAroundEqualsIsAccepted` Refs: WOO-552, WOO-553 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(sync): lock Barry's rapport-versie example as explicit test — WOO-553 Add `testFilenameWithSemicolonAndSpaceInsideQuotedValuePreservesFilename` covering Barry's exact string from his PR #1840 review: attachment; filename="rapport; versie 2.pdf" The existing `testFilenameWithSemicolonInsideQuotedValuePreservesFilename` already exercises the same tokenizer path via `foo;bar.pdf`, but the new test locks the concrete example he raised so the guarantee is explicit in the suite, not only implied. Also mirrored to the WOO stable hotfix branch alongside the tokenizer backport (WOO-552 hotfix cut of v0.2.24-woo-2). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(sync): correct SynchronizationService namespace in test — WOO-553 The test imported OCA\OpenConnector\Service\SynchronizationService, but integriq's class lives under OCA\Integriq\Service\SynchronizationService (matching every other test in this directory). ReflectionClass failed on the missing class, producing 14 PHPUnit errors across every PHP/NC matrix cell and cascading into the Quality Report failure. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(sync): honour RFC 9110 quoted-pairs in Content-Disposition tokenizer — WOO-553 splitHeaderParameters() toggled $inQuotes on every '"', so an escaped quote inside a quoted filename (\") flipped the state and a following ';' split the value. Skip backslash quoted-pairs as a unit inside quotes, and unescape them (\" → ", \\ → \) in the plain filename via new unquoteHeaderValue(). Review case from PR #1840: attachment; filename="rapport \"final\"; versie 2.pdf" now yields: rapport "final"; versie 2.pdf +3 unit tests (17/17 green). * fix(sync): harden Content-Disposition parsing per Strict re-review — WOO-553 Addresses the six findings of the round-3 self-review on PR #1840: - Empty parameter values no longer yield '': an empty filename* (UTF-8'') cannot clobber a usable plain filename, and filename="" / filename= return null so the URL/MIME fallback runs instead of saveFile('') aborting the sync. - An undecodable second filename* no longer overwrites a decoded one. - filename* is unquoted before decoding (sloppy servers send filename*="UTF-8''x.pdf"); malformed ext-values are now logged like unsupported charsets instead of failing silently. - Content-Disposition is looked up case-insensitively via new getHeaderValue() so lowercase HTTP/2 headers reach the parser; the body-decoding guard uses the same lookup. - Test docblock lists all covered scenarios; the path-traversal test comment names the real guard (NC core Filesystem::isValidPath()). +6 unit tests (23/23 green). * fix(sync): enforce decodable filename* and harden header lookup (review round 4) — WOO-553 - decodeRfc5987ExtendedValue(): rawurldecode() never fails, so reject results that are invalid UTF-8 or contain control characters (%00); a valid plain filename then wins instead of garbage. - Empty-checks on filename*/filename are trim-based (whitespace-only counts as absent). - getHeaderValue(): accepts mixed (persisted scalar headers → null), skips unusable values (empty list, null, nested array, false) and keeps scanning for a differently-cased duplicate; docblock widened. - Content-Type fallback in getFilenameFromHeaders() now uses the same helper instead of two hard-coded casings. - Malformed-ext-value log carries valueLength, not the raw value. - Docblock prose updated (parser fallback conditions, test helper). +6 unit tests (29/29 green). * style(sync): capitalise inline comment (phpcs Squiz.Commenting.InlineComment) — WOO-553 * style(sync): review round 5 nits — comment period, distinct log keys, named args on mb_check_encoding — WOO-553 --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hydra-gates v1.15.0 -> v1.16.0 nc-vue 2.37.0 -> 2.39.0 Lock-only: both packages are already declared with caret ranges that permit these versions, so nothing about what this app ACCEPTS changes - only what it currently resolves to. Opened by the weekly fleet shared-dependency bump, because a lock nobody re-resolves is a pin nobody chose. Merging is gated by this repository's own suite, deliberately: taking hydra-gates v1.8.1 added patchObject() to a published interface, which is a load-time fatal for any concrete double that implements it without the method. CI is the only thing that can tell a safe bump from that. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
First capability of the gate-19 review. The 21 untagged scenarios here
were not an annotation backlog: a third of them describe a manifest that
no longer exists, and tagging those would have cemented the staleness.
Stale, corrected against the live manifest:
"All 15 menu entries are present" ADR-097 grouped the nav. There are
10 top-level entries, five of them
groups, and Import and Settings are
not among them.
"Settings section entries carry the surviving chrome moved from
section field" section "settings" to "footer"
under ADR-114.
"Documentation entry uses href" the href moved with the rename, to
integriq.conduction.nl.
"Primary nav entries have route" named a flat id list, half of it
gone. The invariant survives the
regrouping and is what is asserted.
"version is 1.0.0 for D1" the manifest is 2.1.0. The durable
claim is that the version tracks the
schema generation.
Import / Settings pages INVERTED rather than deleted, per
the fleet pattern: the scenario now
asserts the page is absent, so its
accidental return fails rather than
passing silently.
Three scenarios gained real coverage rather than a waiver, because
anchoring without an assertion is the defect this gate exists to catch:
- every domain leaf sits inside a group, walking the whole main list.
Asserting it for one or two leaves would not establish "no loose
leaves", which is what the scenario claims.
- Documentation leaves the app, pinning the href in the rendered DOM.
- every navigating menu entry has a route that names a real page,
walking children too, with a positive control so an empty menu cannot
satisfy it vacuously.
Both new browser tests were verified to FAIL when inverted.
The remaining 13 carry reason-bearing `@e2e exclude`: source-text greps
over main.js, package.json keys, JSON type assertions, and three
validateManifest cases that need a deliberately INVALID manifest, which
an e2e against a running app cannot produce. Each reason names what does
cover it instead.
gate-19 goes 334 to 318. All 78 applicable hydra gates green with the
runner CI resolves, and all 78 ran; check:specs exit 0; app-chrome 14
passed, manifest-pages 52 passed.
…fest spec(app-manifest): correct the stale scenarios and annotate the rest
… code that was never built
Second capability of the gate-19 review, and the largest at 46 scenarios.
Almost none of it is browser-observable: it is the OpenRegister cutover,
so the claims are about files being absent, constructors carrying no
mapper, quality gates rejecting bad input, and services calling
ObjectService.
35 carry reason-bearing `@e2e exclude`, each naming what actually holds
it, and each checked rather than asserted:
file-absence and shape claims verified against the tree. lib/Db is
gone, ImportController and
ExportController do not exist, no file
names ObjectMapperFacade, and the only
two *Mapper.php left are StUFFieldMapper
and FormFieldMapper, which are FIELD
mappers, not entity QBMappers.
quality-gate scenarios they work by feeding the gate
deliberately bad input, which an e2e
against a running app cannot produce.
service behaviours named against the PHPUnit class that
covers each: JobServiceTest,
CallServiceTest, SourcesControllerTest.
migration scenarios occ upgrade runs a migration and its
repair step; a browser session never
reaches that path.
SIX SCENARIOS DESCRIBE CODE THAT DOES NOT EXIST, and are flagged rather
than annotated, because a waiver would have hidden that:
SourceService no such class under lib/. Three scenarios read
and delete Sources through it.
SlugTranslatorService appears ONLY in this spec. Two scenarios.
SettingsController exposes `rebase`, not the `applyRetention` the
scenario names.
Annotating those would have recorded coverage for a design that never
shipped. They need rewriting against what the cutover actually landed,
which is a change of its own.
One scenario is anchored, after reading the body rather than grepping:
`synchronization-run-endpoint-is-still-reachable` on the pipeline test in
synchronization-workflow.spec.ts, which POSTs to
`/synchronizations/{id}/run` and asserts the endpoint RESOLVES the sync
rather than 404ing. That is the scenario's THEN.
Four are left visible with a reason saying why no test establishes them:
the Sources wire shape, the OR create and export routes, and the
dashboard's dataSource resolution. Each names the test that touches the
subject without proving the claim.
gate-19 goes 318 to 278. All 77 applicable hydra gates green with the
runner CI resolves, and all 77 ran; check:specs exit 0.
spec(direct-or-usage): annotate 40 scenarios and flag 6 that describe code that was never built
…scribe deleted code
Third capability of the gate-19 review. All 28 scenarios are about the
legacy-to-OpenRegister storage transition, and that transition is done,
so the spec documents a state the app has left.
Seven describe machinery that no longer exists, and are FLAGGED rather
than annotated, because a waiver would record coverage for deleted code:
find(int) / createFromArray / delete-invalidates-cache / append-only on
log mappers the entity mappers are gone; lib/Db does not exist
flag routes to legacy path, flag routes to facade path, rollback to
legacy path no file under lib/ carries a legacy path, and none
names ObjectMapperFacade
The flag itself SURVIVES, so those scenarios were not swept with the
rest: `storage_migrated` is still read, by SynchronizationContractProvider
among others.
21 carry reason-bearing `@e2e exclude`, split by what actually holds
them: occ upgrade and console behaviours for the migration, its repair
step and the retry command; PHPUnit for the JSON build, UUID
preservation, PK resolution and null-owner paths.
One was verified TRUE rather than waived. "Legacy tables present after
migration" holds: measured 2026-09-07 on a migrated instance, 15
oc_openconnector* tables are still there, because
Version2Date20260520000099 drops each only once it is EMPTY and refuses
on any that is not. The exclude reason records the measurement, not an
assumption.
gate-19 goes 278 to 257. All 77 applicable hydra gates green with the
runner CI resolves, and all 77 ran; check:specs exit 0.
spec(storage-migration): the cutover completed, so seven scenarios describe deleted code
Fourth capability of the gate-19 review. 24 scenarios about the Vue
rewrite, and the same shape as the three before it: mostly static claims
about what the migration deleted, plus a handful that no longer hold.
Four are stale, flagged rather than annotated:
"All 13 manifest menu items" ADR-097 grouped the nav. 10 top-level
entries, five of them groups.
"Import page retains custom there is no Import page.
type"
"Settings page is reachable there is no Settings page. ADR-114 puts
via nav" settings behind the nav foldout, which
links to /settings/admin/integriq.
"settings.js webpack entry is THE ENTRY IS STILL THERE, ON PURPOSE.
absent" webpack.config.js keeps `settings` to
build src/settings.js for the ADR-023
admin panel rendered by
templates/settings/admin.php, which is
a different surface from the SPA this
change migrated. The scenario is not
unmet; it is wrong.
Four are anchored on manifest-pages.spec.ts, after reading the body: the
mount loop drives every manifest route and asserts the router matched,
that content rendered inside #app-content and that no console error
fired, while `manifest page table is complete and current` pins each
page's declared type. Together those establish that a page of the
declared type renders at its route, which is what the navigation,
Sources, Rules and MappingDetail scenarios claim.
17 carry reason-bearing `@e2e exclude`, each verified rather than
asserted: src/router/index.js, src/navigation, Modals.vue and any
src/-root widget JS are all confirmed absent, and zero .php files remain
under src/. The build and lint scenarios name the CI command that holds
them instead.
Four are left visible with a reason saying why nothing establishes them:
sidebars per page, the Nextcloud Dashboard widget bundles, the
flow-token surviving a synchronization trigger, and the create-source
form opening from CnIndexPage. The last is the interesting one: the CRUD
spec does create a Source through the UI, but it drives the whole round
trip rather than asserting the form opens, so anchoring it would
overclaim.
gate-19 goes 257 to 237. All 77 applicable hydra gates green with the
runner CI resolves, and all 77 ran; check:specs exit 0.
spec(vue-rewrite): four scenarios outlived the migration they describe
…e canvas (#1928) tests/e2e/regression/flows-editor.spec.ts:153 has failed on development since at least 2026-09-07, always the same way: a 60s test timeout, then 'Target page, context or browser has been closed' reported from the NEXT call inside openFlowActionsMenu. That reads as a hung editor. It is not. The page snapshot in the trace shows four buttons named Actions: three inside the flow canvas that are in the accessibility tree but zero-size, and the real one in the sidebar header next to the New flow heading. The helper matched all four by accessible name and clicked .first(), which is one of the canvas ones. Clicking a non-actionable element waits, and the click carried no timeout, so that wait was the whole test budget. Two changes. The sidebar-scoped selector moves ahead of the unscoped name match, because CnFlowSidebar puts the 'Flow actions' aria-label on the NcActions wrapper while NcAppSidebar wraps that slot in an NcActions of its own, so the button a user clicks is named plainly 'Actions'. And every candidate is now filtered to visible and clicked with a 5s timeout, so a wrong trigger costs 5s and the loop moves on instead of ending the test. Verified against a fixture that reproduces the DOM: three zero-size buttons named Actions plus a visible sidebar one. The old helper exceeds a 15s budget; the new one opens the menu in 191ms. prettier --check passes on the file. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
… suite (#1929) The test bootstrap guard added earlier today loads lib/base.php only when config/config.php declares installed => true. That part is right and stays. The same change also made the bootstrap exit(1) when the tree is installed and base.php throws part-way, and humaniq proved that wrong: its CI Nextcloud is installed, base.php dies on a Doctrine constant its vendor and the server disagree about, and all six PHPUnit legs went red on a suite that passes. Aborting was the wrong trade. Reaching a half-built container needs an autowiring lookup, this app has none in lib, and phpunit.xml caps every run at 2G. So a mid-boot failure is now loud rather than fatal: it says the container is unreliable and lets the pure unit tests run. The not installed branch is untouched. It still skips base.php and runs with composer autoload and stubs only, which is the case the guard exists for. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
…nrun collection Two gates landed in hydra-gates at @main between this morning's runs and now red every integriq build. Neither is caused by the change that surfaced them: the previous development run's gates job was a clean success. gate-113 exclusion-evidence. Three @e2e exclusions in the flow-orchestration spec named `tests/e2e/ci/flow-controls.spec.ts`. That file has never existed in this repo, so all three read as verified and were not. They now state what actually covers the scenario, which for two of them is the engine's unit tests and for the third is nothing at all. Deliberately NOT repointed at tests/e2e/spec-coverage/flow-orchestration.spec.ts. That file covers the flows index, the canvas, the trace timeline and replay safety. It does not touch a run log or its empty state, so citing it would have replaced one false claim with another, which is the exact thing this gate exists to catch. gate-112 newman-reach. tests/integration/integriq.postman_collection.json is a 58-request contract suite sitting outside newman-collection-path, so CI has never run it. The collection newman does run is tests/postman (101 requests, chain E). It is marked `@newman exclude` rather than moved, because pointing newman at it would start executing 58 requests that have never run in CI. That changes what CI verifies and is a separate decision for whoever owns the suite. The marker was inserted as text rather than by reserialising the JSON: the diff is one line changed, not the whole file. Verified against the gate version CI actually resolves. Running the runner from ConductionNL/.github@main rather than the vendored copy gives 79 PASS, zero FAIL, exit 0 — the vendored copy does not carry gates 112 or 113 at all, which is why they could not be seen locally before.
…tations Stop citing a test that has never existed, and mark the unrun Postman collection
#1948) flows-editor.spec.ts still timed out after the previous fix, and the trace says why in one line: the toolbar Save click was retried 96 times against <div class="modal-wrapper"> from <div role="dialog" aria-modal="true"> CnFlowSettingsModal is an NcDialog. It stays open until something closes it, and its wrapper covers the page. The test opened it, filled the name, and then clicked the toolbar behind it. Playwright reported the button as visible, enabled and stable each time, so the run looked like a hung editor rather than a click landing on an overlay. The previous fix was not wrong, it was incomplete: it got the Actions menu opening, which moved the failure from the menu to the Save click. This closes the dialog and waits for it to go. Verified: prettier exit 0. The proof this makes the spec green is the development push run, since E2E runs only there. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
…#1951) Third and I hope last turn on this spec. Each attempt moved the failure forward, which is how I know the earlier ones were right as far as they went. - The original 60s timeout was a hidden canvas button eating the click budget. - Fixing that got the Actions menu open and moved the failure to the toolbar Save, which the still-open dialog was intercepting. - Dismissing the dialog with Escape does not dismiss it. This run watched toBeHidden resolve the dialog as visible 24 times over 10s, then failed at 20.7s instead of timing out at 60s. The page snapshot in that trace lists the control by name: - dialog "Flow settings": - button "Close" So click it. The lesson each time was the same one: read the snapshot in error-context.md before theorising, because it says what is on the page. Verified: prettier exit 0. The proof is the development push run, since E2E runs only there. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Supersedes #1941. vitest 5 stops before a single test runs: Cannot find package 'vite' imported from node_modules/vitest/... vitest 5 expects `vite` to be resolvable from the project; 4 did not. This app has never declared it, so the bump cannot land on its own. Adding `vite ^8.2.2`, the version versioniq already carries — which is why versioniq's identical bump went green while this one did not. This app declares no `@vitest/coverage-v8`, so unlike decidiq, filinq, buildiq and dossiq there is no exact-pinned peer to move in step here. Verified locally rather than left to CI: `npm install` clean, then `npm run test:unit` on vitest 5.0.0, all passing, exit 0. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Bumps [webpack](https://github.com/webpack/webpack) from 5.110.2 to 5.110.3. - [Release notes](https://github.com/webpack/webpack/releases) - [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md) - [Commits](webpack/webpack@v5.110.2...v5.110.3) --- updated-dependencies: - dependency-name: webpack dependency-version: 5.110.3 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [eslint](https://github.com/eslint/eslint) from 10.9.1 to 10.10.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](eslint/eslint@v10.9.1...v10.10.0) --- updated-dependencies: - dependency-name: eslint dependency-version: 10.10.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…1944) Bumps [jest-environment-jsdom](https://github.com/jestjs/jest/tree/HEAD/packages/jest-environment-jsdom) from 30.5.0 to 30.5.1. - [Release notes](https://github.com/jestjs/jest/releases) - [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/jestjs/jest/commits/v30.5.1/packages/jest-environment-jsdom) --- updated-dependencies: - dependency-name: jest-environment-jsdom dependency-version: 30.5.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ruben van der Linde <ruben@conduction.nl>
Bumps [sass-loader](https://github.com/webpack/sass-loader) from 17.0.0 to 17.0.1. - [Release notes](https://github.com/webpack/sass-loader/releases) - [Changelog](https://github.com/webpack/sass-loader/blob/main/CHANGELOG.md) - [Commits](webpack/sass-loader@v17.0.0...v17.0.1) --- updated-dependencies: - dependency-name: sass-loader dependency-version: 17.0.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ruben van der Linde <ruben@conduction.nl>
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 26.4.0 to 26.4.1. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 26.4.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ruben van der Linde <ruben@conduction.nl>
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.62.1 to 1.63.0. - [Release notes](https://github.com/microsoft/playwright/releases) - [Commits](microsoft/playwright@v1.62.1...v1.63.0) --- updated-dependencies: - dependency-name: "@playwright/test" dependency-version: 1.63.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ruben van der Linde <ruben@conduction.nl>
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan-phar-composer-source) from 2.2.9 to 2.2.13. - [Commits](https://github.com/phpstan/phpstan-phar-composer-source/commits) --- updated-dependencies: - dependency-name: phpstan/phpstan dependency-version: 2.2.13 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ruben van der Linde <ruben@conduction.nl>
…1935) Bumps [conduction/hydra-gates](https://github.com/ConductionNL/.github) from 1.16.0 to 1.16.1. - [Release notes](https://github.com/ConductionNL/.github/releases) - [Commits](ConductionNL/.github@v1.16.0...v1.16.1) --- updated-dependencies: - dependency-name: conduction/hydra-gates dependency-version: 1.16.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ruben van der Linde <ruben@conduction.nl>
Bumps [web-token/jwt-library](https://github.com/web-token/jwt-library) from 4.2.1 to 4.2.2. - [Commits](web-token/jwt-library@4.2.1...4.2.2) --- updated-dependencies: - dependency-name: web-token/jwt-library dependency-version: 4.2.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ruben van der Linde <ruben@conduction.nl>
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.4.14 to 3.4.15. - [Release notes](https://github.com/cure53/DOMPurify/releases) - [Commits](cure53/DOMPurify@3.4.14...3.4.15) --- updated-dependencies: - dependency-name: dompurify dependency-version: 3.4.15 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ruben van der Linde <ruben@conduction.nl>
Bumps [stylelint](https://github.com/stylelint/stylelint) from 17.14.1 to 17.15.0. - [Release notes](https://github.com/stylelint/stylelint/releases) - [Changelog](https://github.com/stylelint/stylelint/blob/main/CHANGELOG.md) - [Commits](stylelint/stylelint@17.14.1...17.15.0) --- updated-dependencies: - dependency-name: stylelint dependency-version: 17.15.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [sass](https://github.com/sass/dart-sass) from 1.103.1 to 1.104.0. - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md) - [Commits](sass/dart-sass@1.103.1...1.104.0) --- updated-dependencies: - dependency-name: sass dependency-version: 1.104.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
… client that ships (#1958) The berichtenbox-digital-post-adapter change was already filed and unstarted at 0 of 10 tasks. This does not add an eleventh proposal for the same capability. It fixes four things about the one that exists. THE BLOCKER WAS A BULLET AT THE BOTTOM. It is now the first heading, because the most likely failure of this change is somebody implementing it, seeing green tests, and believing post is delivered. And it was incomplete: it named a PKIoverheid certificate and an OIN, not the Logius BBK OAuth 2.0 client credentials, and not the third blocker at all. PkiOverheidCredentialResolver resolves through OpenRegister's CredentialBrokerService::issueSigningMaterial, which does not exist, so it fails closed for every certificateRef. Even with a purchased certificate, in-process signing stays refused. Digikoppeling is blocked on the same thing. IT PROPOSED A BERICHTENBOX PROVIDER FROM NOTHING while three Berichtenbox classes were on disk: an abstract BerichtenboxClient with four methods, an honest mock that answers signatureValid=false on purpose, a source adapter reading logius.berichtenbox.feature_flag, a catalog descriptor and a DI registration. The binding is now specified over that client, and REQ-DPA-006 forbids a second Berichtenbox code path. TWO ARCHITECTURES WERE ON THE BOOKS. absorb-dossiq-deliveries carries an open task describing this as a provider quintet on the StufZkn pattern. D7 picks the provider seam, says why (digital post has more than one destination), and makes closing that item a task rather than an inference. TWO DEFECTS IN THE SHIPPED CODE ARE NOW SPECIFIED. The feature flag branches nothing: Application.php binds the mock unconditionally, so setting the flag to 1 changes a log field. And dispatch() takes raw PEM by value, which REQ-DK-005 and ADR-007 forbid and which the sibling Digikoppeling adapter already refuses to do; it becomes a certificateRef resolved inside. Tasks 1, 2, 5, 6 and 7 are unblocked and land first. Task 8 is the live network leg and says BLOCKED, do not start. Spec only. Nothing is implemented here. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…nnector (#1961) * fix(mappings): resolve the default register instead of pinning openconnector MappingsController::saveObject() defaulted its register to the literal 'openconnector'. This app's own repair step MigrateRegisterSlug renames that register to 'integriq', and it runs per instance, so both slugs are live across the estate and a literal is wrong on whichever half has not caught up. The old-slug case is the quiet one. OpenRegister finds no register row, so the write lands nowhere the UI reads back, and the endpoint answers 200 with an object nobody can find again. No exception, no 404, no log line. Measured on the local dev instance, which is fully migrated: the register table holds buildiq, dossiq, integriq, learniq and stackiq, and no openconnector. The default is now a resolution through OpenRegister's published RegisterSlugResolverInterface, and the caller branches on isResolved(). An instance carrying the register under neither slug gets a 409 naming the repair step, not an empty success. A caller-supplied register is still used as given: those come from the register picker, whose options are live openregister_registers rows, so they already carry this instance's own naming. Swept the rest of lib/ with the guard's own patterns and a loose case-insensitive grep for all ten superseded fleet slugs. 30 literals, one register pin, this one. The other 29 are app ids for the credential broker's allowedApps, StUF zender/applicatie identities, JWT issuer claims, a distributed-cache namespace, webhook signature scheme names, repair-step OLD_APP_ID constants and one comment. Each was read, none identifies a register. Also in this change: - conduction/hydra-gates ^1.8.2 to ^1.18.0, which is the first tag that ships the contract. Verified rather than assumed: the lock reads v1.18.0 and vendor/conduction/hydra-gates/hydra-gates/contracts/ holds all four files. - The contracts are read out of vendor, never copied in. shillinq's run copied them and dragged OpenRegister's @SPEC annotations along, which the anchor gate then resolved against the consuming repo: seven findings for paths that were never going to be there. - psalm SCANS the vendored contracts rather than suppressing them. Checked both ways: with the extraFiles entry removed psalm raises 4 UndefinedClass errors, with it psalm resolves the real types and the resolution branch is checked for real. No suppression was added. * chore(mappings): drop em-dashes from the comments this change added The house prose rule bans them. Six sites, all in text added by the previous commit; the em-dashes already in these files predate it and are left alone rather than turned into an unrelated 200-line diff. Also corrects the class docblock of the resolution test, which said three assertions reddened under the mutation where the method docblock beside it recorded the measured four. --------- Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…-subscription-connector Retires the rejected integriq store design (brp-kvk-store-and-subscriptions) with the decision recorded in its archived proposal, and adds registry-subscription-connector scoped to only the adapter half integriq owns. No code changes. Companion: ConductionNL/openregister (registry-subscriptions implementation).
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…0260912131727 chore(sync): carry beta back into development
* feat(e2e): refuse the shared instance unless the run names it The dev box runs one Nextcloud on localhost:8080 that bind-mounts everyone's checkouts. A suite that seeds and deletes objects there is editing a colleague's environment. Removing the hardcoded fallback stopped the suite going there by default, but an environment that still sets NEXTCLOUD_URL to :8080 walked straight in. tests/e2e/shared-instance.ts holds the guard and BASE_URL in tests/e2e/support/baseUrl.ts is now resolved through assertInstancePermitted(), which is the one place a target enters this suite. Aiming at the shared instance still works, it just has to be named: INTEGRIQ_E2E_ALLOW_SHARED_INSTANCE=http://localhost:8080. CI is exempt, where localhost:8080 is the runner's own throwaway instance. The unit test sits in tests/vitest/ because that is the only path the vitest config collects, and it excludes tests/e2e outright. * fix(e2e): stop the guard's unit test failing on CI, and satisfy prettier Two things the first commit got wrong. The guard exempts CI on purpose, and the unit runner runs on a runner, so the three refusal cases passed on a laptop and failed everywhere it mattered: CI=true npx vitest run gave 3 failed, 7 passed. The test now stubs CI and GITHUB_ACTIONS empty in a beforeEach and unstubs them after. Both files also failed prettier --check, which is its own CI leg and is not implied by eslint passing. Re-rendered from the template and reformatted. Verified: 10 of 10 pass with CI=true and without. The whole vitest suite goes from 16 files / 293 tests to 17 files / 303 tests, so the file is collected rather than dark, and playwright still collects 348 tests in 40 files, the same as development.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Automated PR to sync development changes to beta for beta release.
Merging this PR will trigger the beta release workflow.