Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
86edc09
feat(walkthrough): show where flows live, without making anyone build…
Aug 28, 2026
3ed798d
Merge pull request #2974 from ConductionNL/chore/walkthrough-flows-stop
rubenvdlinde Aug 28, 2026
8ee9122
docs: add a local demo environment (openregister-compose.yaml + setup…
rubenvdlinde Aug 28, 2026
43bdf59
feat(flow): attribute every object a run touches to the run, node and…
rubenvdlinde Aug 28, 2026
ab46583
fix(flow): a shipped flow must belong to a tenant, or it imports into…
rubenvdlinde Aug 28, 2026
c8c304e
fix(flow): a session-less import must fall back to the DEFAULT organi…
rubenvdlinde Aug 28, 2026
bb7a45f
feat(setup): a wizard that offers the demo data this app already ship…
rubenvdlinde Aug 29, 2026
6fcaf04
fix(e2e): settle the demo-data decision so the wizard stops masking c…
rubenvdlinde Aug 29, 2026
3eedeea
perf(ci): pilot the changed-path filter and the reduced PR matrix (#2…
rubenvdlinde Aug 29, 2026
93e5d7a
chore(deps): take @conduction/nextcloud-vue 2.22.0 for the flow objec…
rubenvdlinde Aug 29, 2026
f8570ec
fix(import): a register import must not unlink schemas it cannot see
Aug 29, 2026
5808b46
Merge pull request #3017 from ConductionNL/fix/register-import-must-n…
rubenvdlinde Aug 29, 2026
2535e24
fix(e2e): actually run the demo-data spec, and clean up after it (#3018)
rubenvdlinde Aug 29, 2026
b6ac618
chore(deps): @conduction/nextcloud-vue 2.22.0 -> 2.22.1 (#3022)
rubenvdlinde Aug 29, 2026
32c458a
feat(release): allow a release to be dispatched by hand (#3024)
rubenvdlinde Aug 29, 2026
561724c
feat(l10n): the translation toolchain, split out so it can land on it…
rubenvdlinde Aug 29, 2026
19afd57
chore(deps-dev): bump phpstan/phpstan from 2.2.8 to 2.2.9 (#3000)
dependabot[bot] Aug 29, 2026
c4ce0a5
chore(deps-dev): bump nextcloud/ocp from 34.0.2 to 34.0.3 (#2999)
dependabot[bot] Aug 29, 2026
98676ec
chore(deps-dev): bump eslint from 10.8.1 to 10.9.1 (#2998)
dependabot[bot] Aug 29, 2026
ba0ec95
chore(deps-dev): bump phpmetrics/phpmetrics from 2.9.1 to 2.11.0 (#2991)
dependabot[bot] Aug 29, 2026
d998ed5
chore(deps): bump twig/twig from 3.27.1 to 3.28.0 (#2989)
dependabot[bot] Aug 29, 2026
a5746d1
chore(deps): bump webonyx/graphql-php from 15.32.3 to 15.37.2 (#2988)
dependabot[bot] Aug 29, 2026
10d2fdd
chore(deps): bump dompurify from 3.4.13 to 3.4.14 (#2983)
dependabot[bot] Aug 29, 2026
d1593da
chore(deps-dev): bump @nextcloud/browserslist-config from 2.3.0 to 3.…
dependabot[bot] Aug 29, 2026
31352fa
chore(deps): bump apexcharts from 4.7.0 to 7.0.0 (#2996)
dependabot[bot] Aug 29, 2026
60f0046
chore(deps): bump web-token/jwt-library from 3.4.10 to 4.1.9 (#2995)
dependabot[bot] Aug 29, 2026
1acf487
test(e2e): seed the walkthrough marker so the tour cannot intercept c…
rubenvdlinde Aug 29, 2026
6060323
fix(l10n): declare each locale's real plural rule instead of English'…
rubenvdlinde Aug 29, 2026
19a65f6
chore(deps): bump elasticsearch/elasticsearch from 8.19.0 to 9.5.0 (#…
dependabot[bot] Aug 29, 2026
2657356
chore(deps): bump gridstack from 12.6.0 to 13.2.0 (#2990)
dependabot[bot] Aug 29, 2026
11715c0
ci(docs): publish from development, and retire the old hostname (#3032)
rubenvdlinde Aug 29, 2026
5167657
style(e2e): wrap the seeded setItem call to satisfy prettier (#3046)
rubenvdlinde Aug 29, 2026
a0819e3
chore(release): 1.1.8-unstable.20260829125656 (#3044)
github-actions[bot] Aug 29, 2026
63346ab
test(e2e): let the seed disable Nextcloud's first-run wizard (#3050)
rubenvdlinde Aug 29, 2026
b7a9f43
chore(deps): bump zod from 3.25.76 to 4.4.3 and migrate call sites (#…
rubenvdlinde Aug 29, 2026
18a40e7
feat(organisation): consolidate the leaf apps organisation onto OpenR…
rubenvdlinde Aug 29, 2026
12887c6
feat(flow): pin every run to the flow version it started on (#3047)
rubenvdlinde Aug 29, 2026
c6191be
test(e2e): check the fleet's docs sites render, not just answer 200 (…
rubenvdlinde Aug 29, 2026
bebec6c
fix(flow): the editor's Run now is the documented draft exception (#3…
rubenvdlinde Aug 29, 2026
fa111f2
chore(deps): @conduction/nextcloud-vue 2.22.1 -> 2.24.1 (#3058)
rubenvdlinde Aug 29, 2026
cc27e20
fix(migration): drop the Dutch verwerkingsactiviteiten columns in cha…
rubenvdlinde Aug 29, 2026
1fe0615
fix(flow): version every flow, surface refusals as 409, and take nc-v…
rubenvdlinde Aug 29, 2026
d03e3bb
chore(deps): @conduction/nextcloud-vue 2.24.1 -> 2.24.2 (#3066)
rubenvdlinde Aug 29, 2026
83078ec
fix(validation): return the 400 instead of 500-ing on a message-only …
rubenvdlinde Aug 29, 2026
de14788
fix(e2e): the flow suite must clean up after itself (#3070)
rubenvdlinde Aug 29, 2026
580f227
fix(calc): a sequence-bearing identifier must survive an update (#3075)
rubenvdlinde Aug 29, 2026
81e1aa7
chore(release): sync beta back into development (#3074)
rubenvdlinde Aug 29, 2026
1e5b8ed
chore(release): merge beta history into development
Aug 29, 2026
524c823
fix: seven orphaned capabilities — two false, two forbidden, three wi…
rubenvdlinde Aug 29, 2026
104f8ca
Merge pull request #3077 from ConductionNL/chore/sync-beta-history
rubenvdlinde Aug 29, 2026
8a5eef3
fix(flow): delete a flow's version rows with the flow (#3080)
rubenvdlinde Aug 29, 2026
ecce894
chore(deps-dev): migrate the stylelint stack to the new config majors…
rubenvdlinde Aug 29, 2026
0131ef7
fix(flow): sweep the version rows that were orphaned before the casca…
rubenvdlinde Aug 29, 2026
92c7fa9
chore(release): 1.1.9-unstable.20260829214947 (#3085)
github-actions[bot] Aug 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -325,3 +325,23 @@ jobs:
# The fleet had 30,459 such strings, so this records the current count and
# fails only when it GROWS — burning it down stays an ordinary PR.
frontend-checks: '["check:specs", "test:l10n", "format", "check:schema-l10n", "check:l10n-js"]'
# ── Cost controls (see ConductionNL/.github#596, #599) ───────────────
#
# The fleet's CI was not slow, it was QUEUED. A Code Quality run does
# ~38 job-minutes of work and its longest job is 16 minutes, but the
# median run took 78.5 minutes wall clock: one poll on 2026-08-27 found
# 53 jobs running against the account's 60-job ceiling, 1,528 queued
# behind them, and a run that had waited 425 minutes to start. ~96% of
# the wall clock was queueing, so the levers below remove DEMAND rather
# than making any check faster.
# Run PHPUnit and Playwright only when the diff could change their
# verdict. They are 29 of the ~38 job-minutes a run spends, and 58% of
# sampled fleet commits touched no .php/.js/.ts/.vue file at all. Fails
# safe TOWARDS running, and a filtered skip is a declared state in the
# Quality Report, distinct from both a pass and a missing verdict.
enable-path-filtering: true
# PR-time PHPUnit runs the primary PHP against the newest declared
# Nextcloud; the full matrix still runs on every push to a default
# branch and on the release PR into beta. Breadth moves from
# per-commit to per-merge, it is not dropped.
reduce-pr-matrix: true
28 changes: 25 additions & 3 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,35 @@
name: Documentation

# Publishes the docs site to the Cloudflare Worker that serves it.
#
# TRIGGERS ON `development`, NOT ON A `documentation` BRANCH. This file used to
# listen on a branch called `documentation`; nobody has pushed to one since
# 2026-05-25, so the site simply stopped being rebuilt while every docs change
# merged to development satisfied its review and published nothing.
on:
push:
branches: [documentation]
branches: [development]
pull_request:
branches: [documentation]
branches: [development]

jobs:
deploy:
uses: ConductionNL/.github/.github/workflows/documentation.yml@main
# A reusable workflow receives NO secrets by default. Without this block the
# callee's publish step finds CF_API_TOKEN empty, skips itself on its own
# `if:` guard, and the run finishes GREEN having changed nothing -- the
# failure that left the fleet's docs sites on May builds. The names are the
# same on both sides; the org secrets really are CF_API_TOKEN/CF_ACCOUNT_ID.
secrets:
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
with:
cname: openregisters.app
cname: openregister.conduction.nl
# EVERY host this worker answers on, in FULL: wrangler reconciles the
# worker's triggers against this list, so a host left out is REMOVED and
# goes dark.
docs-hosts: openregister.conduction.nl
# PINNED. Deriving the name is how a deploy goes green and reaches
# nobody: wrangler creates the derived worker and publishes there while
# the custom domains keep routing to the real one.
worker-name: openregister-docs
7 changes: 7 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ name: Release
on:
push:
branches: [main, beta, development]
# Lets a human force a release without pushing — the shared workflow already
# expects this: its skip guard is written `github.event_name != 'push' || …`
# precisely so a dispatch is "deliberately never skipped … including of a
# commit this guard would refuse". 18 of the 21 fleet apps already declare it;
# this repo was one of the three that did not, so the only way to trigger a
# release here was to push.
workflow_dispatch:

jobs:
unstable:
Expand Down
19 changes: 18 additions & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Open Register drijft apps zoals OpenCatalogi, Procest, Pipelinq en Software Cata

Vrij en open source onder de EUPL-licentie.
]]></description>
<version>1.1.6-beta.20260820205738</version>
<version>1.1.9-unstable.20260829214947</version>
<licence>EUPL-1.2</licence>
<author mail="info@conduction.nl" homepage="https://www.conduction.nl/">Conduction</author>
<namespace>OpenRegister</namespace>
Expand Down Expand Up @@ -194,6 +194,9 @@ Vrij en open source onder de EUPL-licentie.
<!-- Rewrite before the index is derived: the index is built FROM node
types, so the types should already be the current ones. -->
<step>OCA\OpenRegister\Repair\MigrateRenamedFlowNodeTypes</step>
<!-- Versions FIRST: the trigger index is derived from a flow's PUBLISHED
version, so a flow with no version rows yet would index nothing. -->
<step>OCA\OpenRegister\Repair\BackfillFlowVersions</step>
<step>OCA\OpenRegister\Repair\BackfillFlowTriggerIndex</step>
<step>OCA\OpenRegister\Repair\InitializeFlowActions</step>
<step>OCA\OpenRegister\Repair\MigrateNotificationSubscriptionsToUserConfig</step>
Expand Down Expand Up @@ -227,6 +230,16 @@ Vrij en open source onder de EUPL-licentie.
column while every read looks at the new one and finds null —
no error, no data loss, invisible to the suite. -->
<step>OCA\OpenRegister\Repair\RenameDutchColumns</step>
<!-- AUDIT CHAIN SEED v1 → v2. The flow-attribution fields joined the
canonical JSON the hash covers, so every stored hash has to be
recomputed; ADR-003 Rule 4 requires that be a verify-then-reseal
migration rather than an in-place edit. The step verifies the
OLD chain against the frozen v1 canonicaliser and records the
verdict BEFORE re-sealing — after the re-seal, the difference
between an intact chain and a tampered one no longer exists.
Post-migration, because it needs the flow_* columns the schema
migration adds. -->
<step>OCA\OpenRegister\Repair\RechainAuditTrailForFlowAttribution</step>
</post-migration>
<install>
<step>OCA\OpenRegister\Repair\ReconcileDeclaredBackgroundJobs</step>
Expand All @@ -240,6 +253,9 @@ Vrij en open source onder de EUPL-licentie.
<!-- Rewrite before the index is derived: the index is built FROM node
types, so the types should already be the current ones. -->
<step>OCA\OpenRegister\Repair\MigrateRenamedFlowNodeTypes</step>
<!-- Versions FIRST: the trigger index is derived from a flow's PUBLISHED
version, so a flow with no version rows yet would index nothing. -->
<step>OCA\OpenRegister\Repair\BackfillFlowVersions</step>
<step>OCA\OpenRegister\Repair\BackfillFlowTriggerIndex</step>
<step>OCA\OpenRegister\Repair\InitializeFlowActions</step>
<step>OCA\OpenRegister\Repair\SeedDirectoryVirtualSchemas</step>
Expand Down Expand Up @@ -275,6 +291,7 @@ Vrij en open source onder de EUPL-licentie.
<!-- Annotation-related commands have light DI graphs (mappers + evaluator). -->
<command>OCA\OpenRegister\Command\RematerialiseCalculationsCommand</command>
<command>OCA\OpenRegister\Command\BackfillSystemOwnerCommand</command>
<command>OCA\OpenRegister\Command\ImportSkosCsvCommand</command>
<!-- App-rename repair: re-points registers and schemas at a new owning
app id. Only a DB connection, so the DI graph stays light. -->
<command>OCA\OpenRegister\Command\MigrateSchemaApplicationCommand</command>
Expand Down
27 changes: 27 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
// Federation (cross-instance OCM sharing) — token-scoped serving endpoints.
// #[PublicPage]: the caller is a remote instance authenticated by the
// bearer share token in the URL, not a local session.
// First-time setup wizard (ADR-042) - the standard CnSetupWizard contract.
['name' => 'setup#status', 'url' => '/api/setup/status', 'verb' => 'GET'],
['name' => 'setup#runAction', 'url' => '/api/setup/action/{actionId}', 'verb' => 'POST', 'requirements' => ['actionId' => '[a-z0-9\\-]+']],
['name' => 'federation#objects', 'url' => '/api/federation/{shareToken}/objects', 'verb' => 'GET', 'requirements' => ['shareToken' => '[^/]+']],
['name' => 'federation#object', 'url' => '/api/federation/{shareToken}/objects/{id}', 'verb' => 'GET', 'requirements' => ['shareToken' => '[^/]+', 'id' => '[^/]+']],
['name' => 'federation#meta', 'url' => '/api/federation/{shareToken}/meta', 'verb' => 'GET', 'requirements' => ['shareToken' => '[^/]+']],
Expand Down Expand Up @@ -538,6 +541,20 @@
// for the tenants it exists for. The authorisation that matters is the
// organisation scoping and per-flow guard inside FlowService.
['name' => 'flow#run', 'url' => '/api/flows/{id}/run', 'verb' => 'POST', 'requirements' => ['id' => '[^/]+']],

// Lifecycle. Declared BEFORE the bare `{id}` routes for the same reason
// `{id}/run` is: `id` matches `[^/]+`, so a uuid can never swallow a
// trailing literal segment, but keeping the specific paths first means
// a future looser requirement cannot silently start capturing them.
//
// The VERSION number is `\d+`, not `[^/]+`. Without that,
// `/versions/publish` would match `version` with the literal string
// "publish" and return a 404 for a route that exists.
['name' => 'flow#versions', 'url' => '/api/flows/{id}/versions', 'verb' => 'GET', 'requirements' => ['id' => '[^/]+']],
['name' => 'flow#version', 'url' => '/api/flows/{id}/versions/{version}', 'verb' => 'GET', 'requirements' => ['id' => '[^/]+', 'version' => '\d+']],
['name' => 'flow#publish', 'url' => '/api/flows/{id}/publish', 'verb' => 'POST', 'requirements' => ['id' => '[^/]+']],
['name' => 'flow#draft', 'url' => '/api/flows/{id}/draft', 'verb' => 'POST', 'requirements' => ['id' => '[^/]+']],
['name' => 'flow#deprecate', 'url' => '/api/flows/{id}/deprecate', 'verb' => 'POST', 'requirements' => ['id' => '[^/]+']],
['name' => 'flow#index', 'url' => '/api/flows', 'verb' => 'GET'],
['name' => 'flow#create', 'url' => '/api/flows', 'verb' => 'POST'],
['name' => 'flow#show', 'url' => '/api/flows/{id}', 'verb' => 'GET', 'requirements' => ['id' => '[^/]+']],
Expand Down Expand Up @@ -900,6 +917,9 @@
['name' => 'files#batch', 'url' => '/api/objects/{register}/{schema}/{id}/files/batch', 'verb' => 'POST', 'requirements' => ['id' => '[^/]+']],
['name' => 'files#preview', 'url' => '/api/objects/{register}/{schema}/{id}/files/{fileId}/preview', 'verb' => 'GET', 'requirements' => ['id' => '[^/]+', 'fileId' => '\d+']],
['name' => 'files#updateLabels', 'url' => '/api/objects/{register}/{schema}/{id}/files/{fileId}/labels', 'verb' => 'PUT', 'requirements' => ['id' => '[^/]+', 'fileId' => '\d+']],
// Description and category had NO surface before this: only labels did,
// which is why the gap was easy to miss. `file-actions` specifies all three.
['name' => 'files#updateMetadata', 'url' => '/api/objects/{register}/{schema}/{id}/files/{fileId}/metadata', 'verb' => 'PUT', 'requirements' => ['id' => '[^/]+', 'fileId' => '\d+']],

// Direct file access by ID (authenticated).
['name' => 'files#downloadById', 'url' => '/api/files/{fileId}/download', 'verb' => 'GET', 'requirements' => ['fileId' => '\d+']],
Expand Down Expand Up @@ -1296,6 +1316,12 @@
['name' => 'transfer#index', 'url' => '/api/transfers', 'verb' => 'GET'],
['name' => 'transfer#show', 'url' => '/api/transfers/{id}', 'verb' => 'GET', 'requirements' => ['id' => '[^/]+']],
['name' => 'transfer#create', 'url' => '/api/transfers', 'verb' => 'POST'],
// The archivist's decision. A literal trailing segment, so `{id}` — which
// matches [^/]+ — can never swallow them. Without these two the whole
// e-Depot flow was unreachable: `transfer#create` refuses to dispatch
// anything that is not `approved`, and nothing could set that status.
['name' => 'transfer#approve', 'url' => '/api/transfers/{id}/approve', 'verb' => 'POST', 'requirements' => ['id' => '[^/]+']],
['name' => 'transfer#reject', 'url' => '/api/transfers/{id}/reject', 'verb' => 'POST', 'requirements' => ['id' => '[^/]+']],

// Features & Roadmap menu — GitHub issues proxy (add-features-roadmap-menu).
// GET is a cached read (NoCSRFRequired set via controller attribute, pure read).
Expand All @@ -1313,6 +1339,7 @@
// answered by `show('active')` → 404 for every request.
['name' => 'flowRun#active', 'url' => '/api/flow-runs/active', 'verb' => 'GET'],
['name' => 'flowRun#show', 'url' => '/api/flow-runs/{uuid}', 'verb' => 'GET', 'requirements' => ['uuid' => '[^/]+']],
['name' => 'flowRun#objects', 'url' => '/api/flow-runs/{uuid}/objects', 'verb' => 'GET', 'requirements' => ['uuid' => '[^/]+']],
['name' => 'flowRun#retry', 'url' => '/api/flow-runs/{uuid}/retry', 'verb' => 'POST', 'requirements' => ['uuid' => '[^/]+']],
['name' => 'flowRun#resume', 'url' => '/api/flow-runs/{uuid}/resume', 'verb' => 'POST', 'requirements' => ['uuid' => '[^/]+']],
// Interactive test run (or-flow-partial-run): run synchronously with optional startAt + pins + seed.
Expand Down
9 changes: 7 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
"OCA\\OpenRegister\\": "lib/"
}
},
"autoload-dev": {
"psr-4": {
"OCA\\OpenRegister\\Tests\\": "tests/"
}
},
"repositories": [
{
"type": "vcs",
Expand Down Expand Up @@ -101,7 +106,7 @@
"ddn/sapp": "dev-feat/chained-filter-text-replace#5c406e91254d6936f44372db35f1cc15e5a06c56",
"dompdf/dompdf": "^3.1",
"dragonmantank/cron-expression": "^3.3",
"elasticsearch/elasticsearch": "^v8.14.0",
"elasticsearch/elasticsearch": "^v9.5.0",
"guzzlehttp/guzzle": "^7.0",
"jwadhams/json-logic-php": "^1.5",
"minishlink/web-push": "^9.0",
Expand All @@ -121,7 +126,7 @@
"symfony/yaml": "^6.4 || ^7.0",
"theodo-group/llphant": "^0.9.3",
"twig/twig": "^3.27.0",
"web-token/jwt-library": "^3.4.10",
"web-token/jwt-library": "^4.1.9",
"webonyx/graphql-php": "^15.0",
"zbateson/mail-mime-parser": "^3.0"
},
Expand Down
Loading
Loading