Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
53824ad
chore(sync): carry main back into beta
github-actions[bot] Aug 30, 2026
7ae34af
Merge pull request #1017 from ConductionNL/sync/main-to-beta-20260830…
rubenvdlinde Aug 30, 2026
5ba834a
chore(sync): carry beta back into development
github-actions[bot] Aug 30, 2026
9f165e1
Merge pull request #1018 from ConductionNL/sync/beta-to-development-2…
rubenvdlinde Aug 30, 2026
f45f24c
build(deps-dev): bump node-polyfill-webpack-plugin from 4.0.0 to 4.1.…
dependabot[bot] Aug 30, 2026
c961918
build(deps-dev): bump @vue/compiler-sfc from 3.5.41 to 3.5.42 (#1013)
dependabot[bot] Aug 30, 2026
d3b0dc1
build(deps): bump dompurify from 3.4.13 to 3.4.14 (#1006)
dependabot[bot] Aug 30, 2026
3cedc9c
chore(dependabot): hold back vue-router 5 as well (#1027)
rubenvdlinde Aug 30, 2026
c41f0b8
docs(dependabot): correct the vue-router holdback to what was measure…
rubenvdlinde Aug 30, 2026
7eb1199
build(deps-dev): bump css-loader from 7.1.4 to 7.1.5 (#1012)
dependabot[bot] Aug 31, 2026
c7e15b5
build(deps): bump @nextcloud/dialogs from 7.4.1 to 7.5.0 (#1010)
dependabot[bot] Aug 31, 2026
b59d2bc
build(deps): bump vue from 3.5.40 to 3.5.42 (#1007)
dependabot[bot] Aug 31, 2026
c8cc3b0
fix(deps): resolve the peer chain so npm ci works (#1036)
rubenvdlinde Aug 31, 2026
2076df8
chore(deps): @conduction/nextcloud-vue 2.25.0 -> 2.25.1 (#1038)
rubenvdlinde Aug 31, 2026
dad5534
feat(governance): add the cross-app command seam for governance bodie…
rubenvdlinde Aug 31, 2026
044588b
feat(approval): add the cross-app command seam for approval routes (#…
rubenvdlinde Aug 31, 2026
85108ef
fix(e2e): close the setup wizard so its modal mask stops failing the …
rubenvdlinde Aug 31, 2026
68e673f
chore(release): 1.1.6-unstable.20260831045941 (#1044)
github-actions[bot] Aug 31, 2026
1dd19de
fix(newman): stop asserting a catalogue CI never seeds (#1045)
rubenvdlinde Aug 31, 2026
64c95c6
chore(deps): take @conduction/nextcloud-vue 2.26.0 (#1048)
rubenvdlinde Aug 31, 2026
67e0f6c
chore(deps): take @conduction/nextcloud-vue 2.27.0 (#1050)
rubenvdlinde Aug 31, 2026
aec6caa
fix(e2e): seed the five Goals goals-pages.spec.ts asserts on (#1052)
rubenvdlinde Aug 31, 2026
89e65c5
fix(e2e): match the minutes widget by id, not by a phrase shown twice…
rubenvdlinde Aug 31, 2026
676eee9
fix(e2e): give the viewport-loop test a budget larger than its own wa…
rubenvdlinde Aug 31, 2026
db3dc88
feat(setup): ask which example set to load, instead of planting one o…
rubenvdlinde Aug 31, 2026
24c5d81
Merge pull request #1022 from ConductionNL/development
rubenvdlinde Aug 31, 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
10 changes: 10 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ updates:
ignore:
- dependency-name: "typescript"
update-types: ["version-update:semver-major"]
# vue-router 5 peers `vite: ^7.3.0 || ^8.0.0` and expects a Vite
# toolchain. Measured, not assumed, and the result is SPLIT: integriq and
# zaakafhandelapp fail their build on `Can't resolve 'vue-router'`, while
# openregister and learniq build clean on 5.3.0. Held because a bump that
# breaks some apps and not others cannot be merged unattended, and the
# difference is not yet understood. Adopting it fleet-wide is a
# Vite migration, not a bump. versioniq is already on Vite and is the
# natural pilot.
- dependency-name: "vue-router"
update-types: ["version-update:semver-major"]
- dependency-name: "webpack-cli"
update-types: ["version-update:semver-major"]
- dependency-name: "@babel/core"
Expand Down
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Vrij en open source onder de EUPL-1.2-licentie.
version is <= the stored one, and unlike the SCHEMA path it has no
content-differs escape hatch to fall back on.
-->
<version>1.1.5-unstable.20260830174202</version>
<version>1.1.6-unstable.20260831045941</version>
<licence>EUPL-1.2</licence>
<author mail="info@conduction.nl" homepage="https://www.conduction.nl/">Conduction</author>
<namespace>Decidiq</namespace>
Expand Down
4 changes: 4 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
// 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\\-]+']],
// A `choice` step POSTs its answer here before it advances; the
// `run-action` step that follows reads it back, because runAction()
// carries no body and so cannot carry the answer itself.
['name' => 'setup#saveConfig', 'url' => '/api/setup/config', 'verb' => 'POST'],
['name' => 'settings#getPublicationConfig', 'url' => '/api/settings/publication-config', 'verb' => 'GET'],
['name' => 'settings#setPublicationConfig', 'url' => '/api/settings/publication-config', 'verb' => 'PUT'],

Expand Down
2 changes: 1 addition & 1 deletion l10n/.schema-l10n-baseline.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"uncovered": 1634
"uncovered": 1632
}
15 changes: 14 additions & 1 deletion l10n/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -1738,7 +1738,20 @@ OC.L10N.register(
"No meetings": "No meetings",
"Previous month": "Previous month",
"Table": "Table",
"Today": "Today"
"Today": "Today",
"Source app": "Source app",
"External reference": "External reference",
"App id of the fleet app this route was held on behalf of (e.g. dossiq). Empty for routes authored in decidiq itself. Half of the idempotency key; never an authorization input.": "App id of the fleet app this route was held on behalf of (e.g. dossiq). Empty for routes authored in decidiq itself. Half of the idempotency key; never an authorization input.",
"The producing app's own id for the route this one was migrated from. Paired with sourceApp it identifies the origin, so a repeated command updates this route rather than creating another.": "The producing app's own id for the route this one was migrated from. Paired with sourceApp it identifies the origin, so a repeated command updates this route rather than creating another.",
"consultationType=tender: the winning party recorded when the award decision is made (status=awarded). Decidiq owns publish/manage/award only; authoring is procest and bidding is pipelinq.": "consultationType=tender: the winning party recorded when the award decision is made (status=awarded). Decidiq owns publish/manage/award only; authoring is procest and bidding is pipelinq.",
"schema.org annotation matching the ORI type (ChooseAction / Event / CreativeWork).": "schema.org annotation matching the ORI type (ChooseAction / Event / CreativeWork).",
"consultationType=tender: public tender reference/notice number.": "consultationType=tender: public tender reference/notice number.",
"Association or VvE": "Association or VvE",
"Company board": "Company board",
"Every schema, generated values": "Every schema, generated values",
"Municipality": "Municipality",
"None, I will set this up myself": "None, I will set this up myself",
"Works council": "Works council"
},
"nplurals=2; plural=(n != 1);"
)
15 changes: 14 additions & 1 deletion l10n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1737,7 +1737,20 @@
"No meetings": "No meetings",
"Previous month": "Previous month",
"Table": "Table",
"Today": "Today"
"Today": "Today",
"Source app": "Source app",
"External reference": "External reference",
"App id of the fleet app this route was held on behalf of (e.g. dossiq). Empty for routes authored in decidiq itself. Half of the idempotency key; never an authorization input.": "App id of the fleet app this route was held on behalf of (e.g. dossiq). Empty for routes authored in decidiq itself. Half of the idempotency key; never an authorization input.",
"The producing app's own id for the route this one was migrated from. Paired with sourceApp it identifies the origin, so a repeated command updates this route rather than creating another.": "The producing app's own id for the route this one was migrated from. Paired with sourceApp it identifies the origin, so a repeated command updates this route rather than creating another.",
"consultationType=tender: the winning party recorded when the award decision is made (status=awarded). Decidiq owns publish/manage/award only; authoring is procest and bidding is pipelinq.": "consultationType=tender: the winning party recorded when the award decision is made (status=awarded). Decidiq owns publish/manage/award only; authoring is procest and bidding is pipelinq.",
"schema.org annotation matching the ORI type (ChooseAction / Event / CreativeWork).": "schema.org annotation matching the ORI type (ChooseAction / Event / CreativeWork).",
"consultationType=tender: public tender reference/notice number.": "consultationType=tender: public tender reference/notice number.",
"Association or VvE": "Association or VvE",
"Company board": "Company board",
"Every schema, generated values": "Every schema, generated values",
"Municipality": "Municipality",
"None, I will set this up myself": "None, I will set this up myself",
"Works council": "Works council"
},
"pluralForm": "nplurals=2; plural=(n != 1);"
}
19 changes: 18 additions & 1 deletion l10n/nl.js
Original file line number Diff line number Diff line change
Expand Up @@ -2037,7 +2037,24 @@ OC.L10N.register(
"{n} meeting(s) exceeded the scheduled time": "{n} vergadering(en) overschreden de geplande tijd",
"{n} open action items": "{n} openstaande actiepunten",
"{responded} of {invited} responded": "{responded} van {invited} gereageerd",
"{states} states, {transitions} transitions": "{states} statussen, {transitions} overgangen"
"{states} states, {transitions} transitions": "{states} statussen, {transitions} overgangen",
"Source app": "Bronapplicatie",
"External reference": "Externe referentie",
"App id of the fleet app this route was held on behalf of (e.g. dossiq). Empty for routes authored in decidiq itself. Half of the idempotency key; never an authorization input.": "App-id van de applicatie waarvoor deze route wordt bewaard (bijvoorbeeld dossiq). Leeg voor routes die in decidiq zelf zijn gemaakt. De helft van de idempotentiesleutel; nooit een autorisatiegrond.",
"The producing app's own id for the route this one was migrated from. Paired with sourceApp it identifies the origin, so a repeated command updates this route rather than creating another.": "Het eigen id dat de bronapplicatie gebruikt voor de route waaruit deze is overgenomen. Samen met sourceApp bepaalt het de herkomst, zodat een herhaalde opdracht deze route bijwerkt in plaats van een tweede aanmaakt.",
"Which kind of organisation is this for?": "Voor wat voor organisatie is dit?",
"Pick the organisation this app runs for and you get a worked example of it: bodies, meetings, decisions and templates that fit. Installing the app plants nothing on its own, so this is the only step that adds data. Choose \"None\" on a production install.": "Kies de organisatie waarvoor deze app draait en je krijgt een uitgewerkt voorbeeld: organen, vergaderingen, besluiten en sjablonen die daarbij passen. De app zet zelf niets klaar, dus dit is de enige stap die gegevens toevoegt. Kies \"Geen\" op een productieomgeving.",
"Load the example data": "Laad de voorbeeldgegevens",
"Loads the set you picked. The data is obviously sample data, it is safe to run more than once, and you can delete it afterwards.": "Laadt de set die je koos. De gegevens zijn herkenbaar voorbeeldgegevens, je kunt dit meer dan een keer uitvoeren en je kunt ze daarna verwijderen.",
"consultationType=tender: the winning party recorded when the award decision is made (status=awarded). Decidiq owns publish/manage/award only; authoring is procest and bidding is pipelinq.": "consultationType=tender: de winnende partij, vastgelegd zodra het gunningsbesluit is genomen (status=awarded). Decidiq beheert alleen publiceren, beheren en gunnen; het opstellen gebeurt in procest en het inschrijven in pipelinq.",
"schema.org annotation matching the ORI type (ChooseAction / Event / CreativeWork).": "schema.org-annotatie die hoort bij het ORI-type (ChooseAction / Event / CreativeWork).",
"consultationType=tender: public tender reference/notice number.": "consultationType=tender: het openbare aanbestedings- of publicatienummer.",
"Association or VvE": "Vereniging of VvE",
"Company board": "Raad van bestuur",
"Every schema, generated values": "Elk schema, gegenereerde waarden",
"Municipality": "Gemeente",
"None, I will set this up myself": "Geen, ik richt dit zelf in",
"Works council": "Ondernemingsraad"
},
"nplurals=2; plural=(n != 1);"
)
19 changes: 18 additions & 1 deletion l10n/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -2036,7 +2036,24 @@
"{n} meeting(s) exceeded the scheduled time": "{n} vergadering(en) overschreden de geplande tijd",
"{n} open action items": "{n} openstaande actiepunten",
"{responded} of {invited} responded": "{responded} van {invited} gereageerd",
"{states} states, {transitions} transitions": "{states} statussen, {transitions} overgangen"
"{states} states, {transitions} transitions": "{states} statussen, {transitions} overgangen",
"Source app": "Bronapplicatie",
"External reference": "Externe referentie",
"App id of the fleet app this route was held on behalf of (e.g. dossiq). Empty for routes authored in decidiq itself. Half of the idempotency key; never an authorization input.": "App-id van de applicatie waarvoor deze route wordt bewaard (bijvoorbeeld dossiq). Leeg voor routes die in decidiq zelf zijn gemaakt. De helft van de idempotentiesleutel; nooit een autorisatiegrond.",
"The producing app's own id for the route this one was migrated from. Paired with sourceApp it identifies the origin, so a repeated command updates this route rather than creating another.": "Het eigen id dat de bronapplicatie gebruikt voor de route waaruit deze is overgenomen. Samen met sourceApp bepaalt het de herkomst, zodat een herhaalde opdracht deze route bijwerkt in plaats van een tweede aanmaakt.",
"Which kind of organisation is this for?": "Voor wat voor organisatie is dit?",
"Pick the organisation this app runs for and you get a worked example of it: bodies, meetings, decisions and templates that fit. Installing the app plants nothing on its own, so this is the only step that adds data. Choose \"None\" on a production install.": "Kies de organisatie waarvoor deze app draait en je krijgt een uitgewerkt voorbeeld: organen, vergaderingen, besluiten en sjablonen die daarbij passen. De app zet zelf niets klaar, dus dit is de enige stap die gegevens toevoegt. Kies \"Geen\" op een productieomgeving.",
"Load the example data": "Laad de voorbeeldgegevens",
"Loads the set you picked. The data is obviously sample data, it is safe to run more than once, and you can delete it afterwards.": "Laadt de set die je koos. De gegevens zijn herkenbaar voorbeeldgegevens, je kunt dit meer dan een keer uitvoeren en je kunt ze daarna verwijderen.",
"consultationType=tender: the winning party recorded when the award decision is made (status=awarded). Decidiq owns publish/manage/award only; authoring is procest and bidding is pipelinq.": "consultationType=tender: de winnende partij, vastgelegd zodra het gunningsbesluit is genomen (status=awarded). Decidiq beheert alleen publiceren, beheren en gunnen; het opstellen gebeurt in procest en het inschrijven in pipelinq.",
"schema.org annotation matching the ORI type (ChooseAction / Event / CreativeWork).": "schema.org-annotatie die hoort bij het ORI-type (ChooseAction / Event / CreativeWork).",
"consultationType=tender: public tender reference/notice number.": "consultationType=tender: het openbare aanbestedings- of publicatienummer.",
"Association or VvE": "Vereniging of VvE",
"Company board": "Raad van bestuur",
"Every schema, generated values": "Elk schema, gegenereerde waarden",
"Municipality": "Gemeente",
"None, I will set this up myself": "Geen, ik richt dit zelf in",
"Works council": "Ondernemingsraad"
},
"plurals": null,
"pluralForm": "nplurals=2; plural=(n != 1);"
Expand Down
5 changes: 4 additions & 1 deletion lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
namespace OCA\Decidiq\AppInfo;

use OCA\Decidiq\AppInfo\Registrar\AppHostRegistrar;
use OCA\Decidiq\AppInfo\Registrar\CrossAppEventRegistrar;
use OCA\Decidiq\AppInfo\Registrar\DomainServiceRegistrar;
use OCA\Decidiq\AppInfo\Registrar\IntegrationLeafRegistrar;
use OCA\Decidiq\AppInfo\Registrar\ObjectListenerRegistrar;
Expand All @@ -46,7 +47,8 @@
* than accumulating here:
*
* - {@see AppHostRegistrar} AppHost boilerplate adoption (ADR-040 / ADR-022).
* - {@see DomainServiceRegistrar} decision events, MCP tools, eIDAS, translation.
* - {@see CrossAppEventRegistrar} inbound cross-app command listeners (ADR-041).
* - {@see DomainServiceRegistrar} MCP tools, eIDAS, translation.
* - {@see PlatformIntegrationRegistrar} search, object-write guards, dashboard widget.
* - {@see IntegrationLeafRegistrar} server-side half of the OR integration leaves (ADR-066).
* - {@see ObjectListenerRegistrar} boot()-time filtered object-lifecycle subscriptions.
Expand Down Expand Up @@ -140,6 +142,7 @@ public function register(IRegistrationContext $context): void {
// them.
// @spec openspec/changes/migrate-comments-to-talk-leaf/tasks.md#task-2.1.
// @spec openspec/specs/user-settings/spec.md
(new CrossAppEventRegistrar())->register(context: $context);
(new DomainServiceRegistrar())->register(context: $context);

// Board portal Phase 2 services (audit log, conflict of interest,
Expand Down
93 changes: 93 additions & 0 deletions lib/AppInfo/Registrar/CrossAppEventRegistrar.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

/**
* Decidiq cross-app event registrar.
*
* Wires the typed events other fleet apps dispatch to command Decidiq, and the
* listeners that answer them. Split out of DomainServiceRegistrar because the
* set grew: what was one event/listener pair for decisions is now four, one per
* thing another app can ask Decidiq to do, and each pair costs that class two
* more imports — enough to push it past the coupling threshold, which is how
* this extraction was found rather than guessed.
*
* Per ADR-041 a cross-app COMMAND travels as a typed event. Decidiq's REST
* controllers are the door for EXTERNAL callers; they refuse a request with no
* signed-in user, which is exactly the state an in-process migration runs in.
*
* @category AppInfo
* @package OCA\Decidiq\AppInfo\Registrar
*
* @author Conduction Development Team <info@conduction.nl>
* @copyright 2026 Conduction B.V.
* @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* @link https://conduction.nl
*
* @spec openspec/changes/approval-route-events/specs/approval-route-events/spec.md
*
* SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>
* SPDX-License-Identifier: EUPL-1.2
*/

declare(strict_types=1);

namespace OCA\Decidiq\AppInfo\Registrar;

use OCA\Decidiq\Event\ApprovalActionRequestedEvent;
use OCA\Decidiq\Event\ApprovalRouteRequestedEvent;
use OCA\Decidiq\Event\DecisionRequestedEvent;
use OCA\Decidiq\Event\GovernanceBodyRequestedEvent;
use OCA\Decidiq\Listener\ApprovalActionRequestedListener;
use OCA\Decidiq\Listener\ApprovalRouteRequestedListener;
use OCA\Decidiq\Listener\DecisionRequestedListener;
use OCA\Decidiq\Listener\GovernanceBodyRequestedListener;
use OCP\AppFramework\Bootstrap\IRegistrationContext;

/**
* Registers every inbound cross-app command listener.
*
* @spec openspec/changes/approval-route-events/specs/approval-route-events/spec.md
*/
class CrossAppEventRegistrar {

/**
* The command contract, as event class => listener class.
*
* A map rather than four call sites, so adding a command is one line and
* the whole inbound surface reads as one list.
*
* @var array<class-string, class-string>
*/
private const COMMANDS = [
// Raise a governance Decision for a consumer's object, and conclude it
// back through DecisionConcludedEvent.
DecisionRequestedEvent::class => DecisionRequestedListener::class,

// Hold a governance body — a committee, a board — with its roster.
GovernanceBodyRequestedEvent::class => GovernanceBodyRequestedListener::class,

// Hold a sign-off route, and travel a subject down it. Both delegate to
// the EXISTING ApprovalRouteService: these add a door, not a second
// engine, so the event path and the REST path cannot answer differently
// for the same action.
ApprovalRouteRequestedEvent::class => ApprovalRouteRequestedListener::class,
ApprovalActionRequestedEvent::class => ApprovalActionRequestedListener::class,
];

/**
* Register every command listener.
*
* @param IRegistrationContext $context The registration context.
*
* @return void
*
* @spec openspec/changes/approval-route-events/specs/approval-route-events/spec.md
*/
public function register(IRegistrationContext $context): void {
foreach (self::COMMANDS as $event => $listener) {
$context->registerEventListener(event: $event, listener: $listener);
}

}//end register()

}//end class
25 changes: 0 additions & 25 deletions lib/AppInfo/Registrar/DomainServiceRegistrar.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@

namespace OCA\Decidiq\AppInfo\Registrar;

use OCA\Decidiq\Event\DecisionRequestedEvent;
use OCA\Decidiq\Listener\DecisionRequestedListener;
use OCA\Decidiq\Mcp\DecidiqToolProvider;
use OCA\Decidiq\Service\EIDASSignatureService;
use OCA\Decidiq\Service\IEIDASSignatureService;
Expand Down Expand Up @@ -73,35 +71,12 @@ class DomainServiceRegistrar {
* @spec openspec/specs/mcp-tools/spec.md
*/
public function register(IRegistrationContext $context): void {
$this->registerDecisionEvents(context: $context);
$this->registerMcpToolProvider(context: $context);
$this->registerEidasBindings(context: $context);
$this->registerTranslationAdapter(context: $context);

}//end register()

/**
* The event contract for delegated decisions.
*
* Consumer apps dispatch DecisionRequestedEvent (handled here ->
* createDecision) and listen for DecisionConcludedEvent (emitted from
* DecisionLifecycleService). In-process replacement for the broken
* IntegrationService::getLeaf path.
*
* @param IRegistrationContext $context The registration context
*
* @return void
*
* @spec openspec/changes/decidesk-decision-events/specs/decidesk-decision-events/spec.md
*/
private function registerDecisionEvents(IRegistrationContext $context): void {
$context->registerEventListener(
event: DecisionRequestedEvent::class,
listener: DecisionRequestedListener::class
);

}//end registerDecisionEvents()

/**
* Register DecidiqToolProvider as the MCP tool provider for the AI Chat Companion.
*
Expand Down
Loading
Loading