diff --git a/openspec/changes/add-server-side-kpi-aggregation/builds/build.json b/openspec/changes/add-server-side-kpi-aggregation/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/add-server-side-kpi-aggregation/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/ai-assisted-processing/builds/build.json b/openspec/changes/ai-assisted-processing/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/ai-assisted-processing/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/appointment-scheduling/builds/build.json b/openspec/changes/appointment-scheduling/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/appointment-scheduling/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/automatic-actions/.openspec.yaml b/openspec/changes/archive/2026-05-11-automatic-actions/.openspec.yaml similarity index 100% rename from openspec/changes/automatic-actions/.openspec.yaml rename to openspec/changes/archive/2026-05-11-automatic-actions/.openspec.yaml diff --git a/openspec/changes/automatic-actions/builds/build.json b/openspec/changes/archive/2026-05-11-automatic-actions/builds/build.json similarity index 100% rename from openspec/changes/automatic-actions/builds/build.json rename to openspec/changes/archive/2026-05-11-automatic-actions/builds/build.json diff --git a/openspec/changes/automatic-actions/design.md b/openspec/changes/archive/2026-05-11-automatic-actions/design.md similarity index 100% rename from openspec/changes/automatic-actions/design.md rename to openspec/changes/archive/2026-05-11-automatic-actions/design.md diff --git a/openspec/changes/automatic-actions/proposal.md b/openspec/changes/archive/2026-05-11-automatic-actions/proposal.md similarity index 100% rename from openspec/changes/automatic-actions/proposal.md rename to openspec/changes/archive/2026-05-11-automatic-actions/proposal.md diff --git a/openspec/changes/automatic-actions/specs/automatic-actions/spec.md b/openspec/changes/archive/2026-05-11-automatic-actions/specs/automatic-actions/spec.md similarity index 100% rename from openspec/changes/automatic-actions/specs/automatic-actions/spec.md rename to openspec/changes/archive/2026-05-11-automatic-actions/specs/automatic-actions/spec.md diff --git a/openspec/changes/automatic-actions/tasks.md b/openspec/changes/archive/2026-05-11-automatic-actions/tasks.md similarity index 100% rename from openspec/changes/automatic-actions/tasks.md rename to openspec/changes/archive/2026-05-11-automatic-actions/tasks.md diff --git a/openspec/changes/archive/2026-05-11-beroep-escalation/builds/build.json b/openspec/changes/archive/2026-05-11-beroep-escalation/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/archive/2026-05-11-beroep-escalation/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/beroep-escalation/design.md b/openspec/changes/archive/2026-05-11-beroep-escalation/design.md similarity index 100% rename from openspec/changes/beroep-escalation/design.md rename to openspec/changes/archive/2026-05-11-beroep-escalation/design.md diff --git a/openspec/changes/beroep-escalation/proposal.md b/openspec/changes/archive/2026-05-11-beroep-escalation/proposal.md similarity index 100% rename from openspec/changes/beroep-escalation/proposal.md rename to openspec/changes/archive/2026-05-11-beroep-escalation/proposal.md diff --git a/openspec/changes/beroep-escalation/specs/beroep-escalation/spec.md b/openspec/changes/archive/2026-05-11-beroep-escalation/specs/beroep-escalation/spec.md similarity index 100% rename from openspec/changes/beroep-escalation/specs/beroep-escalation/spec.md rename to openspec/changes/archive/2026-05-11-beroep-escalation/specs/beroep-escalation/spec.md diff --git a/openspec/changes/beroep-escalation/tasks.md b/openspec/changes/archive/2026-05-11-beroep-escalation/tasks.md similarity index 100% rename from openspec/changes/beroep-escalation/tasks.md rename to openspec/changes/archive/2026-05-11-beroep-escalation/tasks.md diff --git a/openspec/changes/archive/2026-05-11-bezwaar-advisory-committee/builds/build.json b/openspec/changes/archive/2026-05-11-bezwaar-advisory-committee/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/archive/2026-05-11-bezwaar-advisory-committee/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/bezwaar-advisory-committee/design.md b/openspec/changes/archive/2026-05-11-bezwaar-advisory-committee/design.md similarity index 100% rename from openspec/changes/bezwaar-advisory-committee/design.md rename to openspec/changes/archive/2026-05-11-bezwaar-advisory-committee/design.md diff --git a/openspec/changes/bezwaar-advisory-committee/proposal.md b/openspec/changes/archive/2026-05-11-bezwaar-advisory-committee/proposal.md similarity index 100% rename from openspec/changes/bezwaar-advisory-committee/proposal.md rename to openspec/changes/archive/2026-05-11-bezwaar-advisory-committee/proposal.md diff --git a/openspec/changes/bezwaar-advisory-committee/specs/bezwaar-advisory-committee/spec.md b/openspec/changes/archive/2026-05-11-bezwaar-advisory-committee/specs/bezwaar-advisory-committee/spec.md similarity index 100% rename from openspec/changes/bezwaar-advisory-committee/specs/bezwaar-advisory-committee/spec.md rename to openspec/changes/archive/2026-05-11-bezwaar-advisory-committee/specs/bezwaar-advisory-committee/spec.md diff --git a/openspec/changes/bezwaar-advisory-committee/tasks.md b/openspec/changes/archive/2026-05-11-bezwaar-advisory-committee/tasks.md similarity index 100% rename from openspec/changes/bezwaar-advisory-committee/tasks.md rename to openspec/changes/archive/2026-05-11-bezwaar-advisory-committee/tasks.md diff --git a/openspec/changes/archive/2026-05-11-bezwaar-decision/builds/build.json b/openspec/changes/archive/2026-05-11-bezwaar-decision/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/archive/2026-05-11-bezwaar-decision/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/bezwaar-decision/design.md b/openspec/changes/archive/2026-05-11-bezwaar-decision/design.md similarity index 100% rename from openspec/changes/bezwaar-decision/design.md rename to openspec/changes/archive/2026-05-11-bezwaar-decision/design.md diff --git a/openspec/changes/bezwaar-decision/proposal.md b/openspec/changes/archive/2026-05-11-bezwaar-decision/proposal.md similarity index 100% rename from openspec/changes/bezwaar-decision/proposal.md rename to openspec/changes/archive/2026-05-11-bezwaar-decision/proposal.md diff --git a/openspec/changes/bezwaar-decision/specs/bezwaar-decision/spec.md b/openspec/changes/archive/2026-05-11-bezwaar-decision/specs/bezwaar-decision/spec.md similarity index 100% rename from openspec/changes/bezwaar-decision/specs/bezwaar-decision/spec.md rename to openspec/changes/archive/2026-05-11-bezwaar-decision/specs/bezwaar-decision/spec.md diff --git a/openspec/changes/bezwaar-decision/tasks.md b/openspec/changes/archive/2026-05-11-bezwaar-decision/tasks.md similarity index 100% rename from openspec/changes/bezwaar-decision/tasks.md rename to openspec/changes/archive/2026-05-11-bezwaar-decision/tasks.md diff --git a/openspec/changes/archive/2026-05-11-bezwaar-hearing/builds/build.json b/openspec/changes/archive/2026-05-11-bezwaar-hearing/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/archive/2026-05-11-bezwaar-hearing/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/bezwaar-hearing/design.md b/openspec/changes/archive/2026-05-11-bezwaar-hearing/design.md similarity index 100% rename from openspec/changes/bezwaar-hearing/design.md rename to openspec/changes/archive/2026-05-11-bezwaar-hearing/design.md diff --git a/openspec/changes/bezwaar-hearing/proposal.md b/openspec/changes/archive/2026-05-11-bezwaar-hearing/proposal.md similarity index 100% rename from openspec/changes/bezwaar-hearing/proposal.md rename to openspec/changes/archive/2026-05-11-bezwaar-hearing/proposal.md diff --git a/openspec/changes/bezwaar-hearing/specs/bezwaar-hearing/spec.md b/openspec/changes/archive/2026-05-11-bezwaar-hearing/specs/bezwaar-hearing/spec.md similarity index 100% rename from openspec/changes/bezwaar-hearing/specs/bezwaar-hearing/spec.md rename to openspec/changes/archive/2026-05-11-bezwaar-hearing/specs/bezwaar-hearing/spec.md diff --git a/openspec/changes/bezwaar-hearing/tasks.md b/openspec/changes/archive/2026-05-11-bezwaar-hearing/tasks.md similarity index 100% rename from openspec/changes/bezwaar-hearing/tasks.md rename to openspec/changes/archive/2026-05-11-bezwaar-hearing/tasks.md diff --git a/openspec/changes/archive/2026-05-11-bezwaar-lifecycle/builds/build.json b/openspec/changes/archive/2026-05-11-bezwaar-lifecycle/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/archive/2026-05-11-bezwaar-lifecycle/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/bezwaar-lifecycle/design.md b/openspec/changes/archive/2026-05-11-bezwaar-lifecycle/design.md similarity index 100% rename from openspec/changes/bezwaar-lifecycle/design.md rename to openspec/changes/archive/2026-05-11-bezwaar-lifecycle/design.md diff --git a/openspec/changes/bezwaar-lifecycle/proposal.md b/openspec/changes/archive/2026-05-11-bezwaar-lifecycle/proposal.md similarity index 100% rename from openspec/changes/bezwaar-lifecycle/proposal.md rename to openspec/changes/archive/2026-05-11-bezwaar-lifecycle/proposal.md diff --git a/openspec/changes/bezwaar-lifecycle/specs/bezwaar-lifecycle/spec.md b/openspec/changes/archive/2026-05-11-bezwaar-lifecycle/specs/bezwaar-lifecycle/spec.md similarity index 100% rename from openspec/changes/bezwaar-lifecycle/specs/bezwaar-lifecycle/spec.md rename to openspec/changes/archive/2026-05-11-bezwaar-lifecycle/specs/bezwaar-lifecycle/spec.md diff --git a/openspec/changes/bezwaar-lifecycle/tasks.md b/openspec/changes/archive/2026-05-11-bezwaar-lifecycle/tasks.md similarity index 100% rename from openspec/changes/bezwaar-lifecycle/tasks.md rename to openspec/changes/archive/2026-05-11-bezwaar-lifecycle/tasks.md diff --git a/openspec/changes/archive/2026-05-11-case-location/builds/build.json b/openspec/changes/archive/2026-05-11-case-location/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/archive/2026-05-11-case-location/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/case-location/design.md b/openspec/changes/archive/2026-05-11-case-location/design.md similarity index 100% rename from openspec/changes/case-location/design.md rename to openspec/changes/archive/2026-05-11-case-location/design.md diff --git a/openspec/changes/case-location/proposal.md b/openspec/changes/archive/2026-05-11-case-location/proposal.md similarity index 100% rename from openspec/changes/case-location/proposal.md rename to openspec/changes/archive/2026-05-11-case-location/proposal.md diff --git a/openspec/changes/case-location/specs/case-location/spec.md b/openspec/changes/archive/2026-05-11-case-location/specs/case-location/spec.md similarity index 100% rename from openspec/changes/case-location/specs/case-location/spec.md rename to openspec/changes/archive/2026-05-11-case-location/specs/case-location/spec.md diff --git a/openspec/changes/case-location/tasks.md b/openspec/changes/archive/2026-05-11-case-location/tasks.md similarity index 100% rename from openspec/changes/case-location/tasks.md rename to openspec/changes/archive/2026-05-11-case-location/tasks.md diff --git a/openspec/changes/case-map-overview/.openspec.yaml b/openspec/changes/archive/2026-05-11-case-map-overview/.openspec.yaml similarity index 100% rename from openspec/changes/case-map-overview/.openspec.yaml rename to openspec/changes/archive/2026-05-11-case-map-overview/.openspec.yaml diff --git a/openspec/changes/archive/2026-05-11-case-map-overview/builds/build.json b/openspec/changes/archive/2026-05-11-case-map-overview/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/archive/2026-05-11-case-map-overview/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/case-map-overview/context-brief.md b/openspec/changes/archive/2026-05-11-case-map-overview/context-brief.md similarity index 100% rename from openspec/changes/case-map-overview/context-brief.md rename to openspec/changes/archive/2026-05-11-case-map-overview/context-brief.md diff --git a/openspec/changes/case-map-overview/design.md b/openspec/changes/archive/2026-05-11-case-map-overview/design.md similarity index 100% rename from openspec/changes/case-map-overview/design.md rename to openspec/changes/archive/2026-05-11-case-map-overview/design.md diff --git a/openspec/changes/case-map-overview/hydra.json b/openspec/changes/archive/2026-05-11-case-map-overview/hydra.json similarity index 100% rename from openspec/changes/case-map-overview/hydra.json rename to openspec/changes/archive/2026-05-11-case-map-overview/hydra.json diff --git a/openspec/changes/case-map-overview/proposal.md b/openspec/changes/archive/2026-05-11-case-map-overview/proposal.md similarity index 100% rename from openspec/changes/case-map-overview/proposal.md rename to openspec/changes/archive/2026-05-11-case-map-overview/proposal.md diff --git a/openspec/changes/case-map-overview/specs/case-map-overview/spec.md b/openspec/changes/archive/2026-05-11-case-map-overview/specs/case-map-overview/spec.md similarity index 100% rename from openspec/changes/case-map-overview/specs/case-map-overview/spec.md rename to openspec/changes/archive/2026-05-11-case-map-overview/specs/case-map-overview/spec.md diff --git a/openspec/changes/case-map-overview/tasks.md b/openspec/changes/archive/2026-05-11-case-map-overview/tasks.md similarity index 100% rename from openspec/changes/case-map-overview/tasks.md rename to openspec/changes/archive/2026-05-11-case-map-overview/tasks.md diff --git a/openspec/changes/archive/2026-05-11-enforcement-lhs/builds/build.json b/openspec/changes/archive/2026-05-11-enforcement-lhs/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/archive/2026-05-11-enforcement-lhs/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/enforcement-lhs/design.md b/openspec/changes/archive/2026-05-11-enforcement-lhs/design.md similarity index 100% rename from openspec/changes/enforcement-lhs/design.md rename to openspec/changes/archive/2026-05-11-enforcement-lhs/design.md diff --git a/openspec/changes/enforcement-lhs/proposal.md b/openspec/changes/archive/2026-05-11-enforcement-lhs/proposal.md similarity index 100% rename from openspec/changes/enforcement-lhs/proposal.md rename to openspec/changes/archive/2026-05-11-enforcement-lhs/proposal.md diff --git a/openspec/changes/enforcement-lhs/specs/enforcement-lhs/spec.md b/openspec/changes/archive/2026-05-11-enforcement-lhs/specs/enforcement-lhs/spec.md similarity index 100% rename from openspec/changes/enforcement-lhs/specs/enforcement-lhs/spec.md rename to openspec/changes/archive/2026-05-11-enforcement-lhs/specs/enforcement-lhs/spec.md diff --git a/openspec/changes/enforcement-lhs/tasks.md b/openspec/changes/archive/2026-05-11-enforcement-lhs/tasks.md similarity index 100% rename from openspec/changes/enforcement-lhs/tasks.md rename to openspec/changes/archive/2026-05-11-enforcement-lhs/tasks.md diff --git a/openspec/changes/archive/2026-05-11-inspection-checklists/builds/build.json b/openspec/changes/archive/2026-05-11-inspection-checklists/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/archive/2026-05-11-inspection-checklists/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/inspection-checklists/design.md b/openspec/changes/archive/2026-05-11-inspection-checklists/design.md similarity index 100% rename from openspec/changes/inspection-checklists/design.md rename to openspec/changes/archive/2026-05-11-inspection-checklists/design.md diff --git a/openspec/changes/inspection-checklists/proposal.md b/openspec/changes/archive/2026-05-11-inspection-checklists/proposal.md similarity index 100% rename from openspec/changes/inspection-checklists/proposal.md rename to openspec/changes/archive/2026-05-11-inspection-checklists/proposal.md diff --git a/openspec/changes/inspection-checklists/specs/inspection-checklists/spec.md b/openspec/changes/archive/2026-05-11-inspection-checklists/specs/inspection-checklists/spec.md similarity index 100% rename from openspec/changes/inspection-checklists/specs/inspection-checklists/spec.md rename to openspec/changes/archive/2026-05-11-inspection-checklists/specs/inspection-checklists/spec.md diff --git a/openspec/changes/inspection-checklists/tasks.md b/openspec/changes/archive/2026-05-11-inspection-checklists/tasks.md similarity index 100% rename from openspec/changes/inspection-checklists/tasks.md rename to openspec/changes/archive/2026-05-11-inspection-checklists/tasks.md diff --git a/openspec/changes/archive/2026-05-11-map-component/builds/build.json b/openspec/changes/archive/2026-05-11-map-component/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/archive/2026-05-11-map-component/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/map-component/design.md b/openspec/changes/archive/2026-05-11-map-component/design.md similarity index 100% rename from openspec/changes/map-component/design.md rename to openspec/changes/archive/2026-05-11-map-component/design.md diff --git a/openspec/changes/map-component/proposal.md b/openspec/changes/archive/2026-05-11-map-component/proposal.md similarity index 100% rename from openspec/changes/map-component/proposal.md rename to openspec/changes/archive/2026-05-11-map-component/proposal.md diff --git a/openspec/changes/map-component/specs/map-component/spec.md b/openspec/changes/archive/2026-05-11-map-component/specs/map-component/spec.md similarity index 100% rename from openspec/changes/map-component/specs/map-component/spec.md rename to openspec/changes/archive/2026-05-11-map-component/specs/map-component/spec.md diff --git a/openspec/changes/map-component/tasks.md b/openspec/changes/archive/2026-05-11-map-component/tasks.md similarity index 100% rename from openspec/changes/map-component/tasks.md rename to openspec/changes/archive/2026-05-11-map-component/tasks.md diff --git a/openspec/changes/parafering-audit-trail/.openspec.yaml b/openspec/changes/archive/2026-05-11-parafering-audit-trail/.openspec.yaml similarity index 100% rename from openspec/changes/parafering-audit-trail/.openspec.yaml rename to openspec/changes/archive/2026-05-11-parafering-audit-trail/.openspec.yaml diff --git a/openspec/changes/archive/2026-05-11-parafering-audit-trail/builds/build.json b/openspec/changes/archive/2026-05-11-parafering-audit-trail/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/archive/2026-05-11-parafering-audit-trail/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/parafering-audit-trail/design.md b/openspec/changes/archive/2026-05-11-parafering-audit-trail/design.md similarity index 100% rename from openspec/changes/parafering-audit-trail/design.md rename to openspec/changes/archive/2026-05-11-parafering-audit-trail/design.md diff --git a/openspec/changes/parafering-audit-trail/proposal.md b/openspec/changes/archive/2026-05-11-parafering-audit-trail/proposal.md similarity index 100% rename from openspec/changes/parafering-audit-trail/proposal.md rename to openspec/changes/archive/2026-05-11-parafering-audit-trail/proposal.md diff --git a/openspec/changes/parafering-audit-trail/specs/parafering-audit-trail/spec.md b/openspec/changes/archive/2026-05-11-parafering-audit-trail/specs/parafering-audit-trail/spec.md similarity index 100% rename from openspec/changes/parafering-audit-trail/specs/parafering-audit-trail/spec.md rename to openspec/changes/archive/2026-05-11-parafering-audit-trail/specs/parafering-audit-trail/spec.md diff --git a/openspec/changes/parafering-audit-trail/tasks.md b/openspec/changes/archive/2026-05-11-parafering-audit-trail/tasks.md similarity index 100% rename from openspec/changes/parafering-audit-trail/tasks.md rename to openspec/changes/archive/2026-05-11-parafering-audit-trail/tasks.md diff --git a/openspec/changes/archive/2026-05-11-pdok-integration/builds/build.json b/openspec/changes/archive/2026-05-11-pdok-integration/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/archive/2026-05-11-pdok-integration/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/pdok-integration/design.md b/openspec/changes/archive/2026-05-11-pdok-integration/design.md similarity index 100% rename from openspec/changes/pdok-integration/design.md rename to openspec/changes/archive/2026-05-11-pdok-integration/design.md diff --git a/openspec/changes/pdok-integration/proposal.md b/openspec/changes/archive/2026-05-11-pdok-integration/proposal.md similarity index 100% rename from openspec/changes/pdok-integration/proposal.md rename to openspec/changes/archive/2026-05-11-pdok-integration/proposal.md diff --git a/openspec/changes/pdok-integration/specs/pdok-integration/spec.md b/openspec/changes/archive/2026-05-11-pdok-integration/specs/pdok-integration/spec.md similarity index 100% rename from openspec/changes/pdok-integration/specs/pdok-integration/spec.md rename to openspec/changes/archive/2026-05-11-pdok-integration/specs/pdok-integration/spec.md diff --git a/openspec/changes/pdok-integration/tasks.md b/openspec/changes/archive/2026-05-11-pdok-integration/tasks.md similarity index 100% rename from openspec/changes/pdok-integration/tasks.md rename to openspec/changes/archive/2026-05-11-pdok-integration/tasks.md diff --git a/openspec/changes/archive/2026-05-11-role-based-step-routing/builds/build.json b/openspec/changes/archive/2026-05-11-role-based-step-routing/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/archive/2026-05-11-role-based-step-routing/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/role-based-step-routing/design.md b/openspec/changes/archive/2026-05-11-role-based-step-routing/design.md similarity index 100% rename from openspec/changes/role-based-step-routing/design.md rename to openspec/changes/archive/2026-05-11-role-based-step-routing/design.md diff --git a/openspec/changes/role-based-step-routing/proposal.md b/openspec/changes/archive/2026-05-11-role-based-step-routing/proposal.md similarity index 100% rename from openspec/changes/role-based-step-routing/proposal.md rename to openspec/changes/archive/2026-05-11-role-based-step-routing/proposal.md diff --git a/openspec/changes/role-based-step-routing/specs/role-based-step-routing/spec.md b/openspec/changes/archive/2026-05-11-role-based-step-routing/specs/role-based-step-routing/spec.md similarity index 100% rename from openspec/changes/role-based-step-routing/specs/role-based-step-routing/spec.md rename to openspec/changes/archive/2026-05-11-role-based-step-routing/specs/role-based-step-routing/spec.md diff --git a/openspec/changes/role-based-step-routing/tasks.md b/openspec/changes/archive/2026-05-11-role-based-step-routing/tasks.md similarity index 100% rename from openspec/changes/role-based-step-routing/tasks.md rename to openspec/changes/archive/2026-05-11-role-based-step-routing/tasks.md diff --git a/openspec/changes/visual-workflow-editor/builds/build.json b/openspec/changes/archive/2026-05-11-visual-workflow-editor/builds/build.json similarity index 100% rename from openspec/changes/visual-workflow-editor/builds/build.json rename to openspec/changes/archive/2026-05-11-visual-workflow-editor/builds/build.json diff --git a/openspec/changes/visual-workflow-editor/design.md b/openspec/changes/archive/2026-05-11-visual-workflow-editor/design.md similarity index 100% rename from openspec/changes/visual-workflow-editor/design.md rename to openspec/changes/archive/2026-05-11-visual-workflow-editor/design.md diff --git a/openspec/changes/visual-workflow-editor/proposal.md b/openspec/changes/archive/2026-05-11-visual-workflow-editor/proposal.md similarity index 100% rename from openspec/changes/visual-workflow-editor/proposal.md rename to openspec/changes/archive/2026-05-11-visual-workflow-editor/proposal.md diff --git a/openspec/changes/visual-workflow-editor/specs/visual-workflow-editor/spec.md b/openspec/changes/archive/2026-05-11-visual-workflow-editor/specs/visual-workflow-editor/spec.md similarity index 100% rename from openspec/changes/visual-workflow-editor/specs/visual-workflow-editor/spec.md rename to openspec/changes/archive/2026-05-11-visual-workflow-editor/specs/visual-workflow-editor/spec.md diff --git a/openspec/changes/visual-workflow-editor/tasks.md b/openspec/changes/archive/2026-05-11-visual-workflow-editor/tasks.md similarity index 100% rename from openspec/changes/visual-workflow-editor/tasks.md rename to openspec/changes/archive/2026-05-11-visual-workflow-editor/tasks.md diff --git a/openspec/changes/archive/2026-05-11-voorstel-management/builds/build.json b/openspec/changes/archive/2026-05-11-voorstel-management/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/archive/2026-05-11-voorstel-management/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/voorstel-management/design.md b/openspec/changes/archive/2026-05-11-voorstel-management/design.md similarity index 100% rename from openspec/changes/voorstel-management/design.md rename to openspec/changes/archive/2026-05-11-voorstel-management/design.md diff --git a/openspec/changes/voorstel-management/proposal.md b/openspec/changes/archive/2026-05-11-voorstel-management/proposal.md similarity index 100% rename from openspec/changes/voorstel-management/proposal.md rename to openspec/changes/archive/2026-05-11-voorstel-management/proposal.md diff --git a/openspec/changes/voorstel-management/specs/voorstel-management/spec.md b/openspec/changes/archive/2026-05-11-voorstel-management/specs/voorstel-management/spec.md similarity index 100% rename from openspec/changes/voorstel-management/specs/voorstel-management/spec.md rename to openspec/changes/archive/2026-05-11-voorstel-management/specs/voorstel-management/spec.md diff --git a/openspec/changes/voorstel-management/tasks.md b/openspec/changes/archive/2026-05-11-voorstel-management/tasks.md similarity index 100% rename from openspec/changes/voorstel-management/tasks.md rename to openspec/changes/archive/2026-05-11-voorstel-management/tasks.md diff --git a/openspec/changes/archive/2026-05-11-vth-workflow-templates/builds/build.json b/openspec/changes/archive/2026-05-11-vth-workflow-templates/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/archive/2026-05-11-vth-workflow-templates/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/vth-workflow-templates/design.md b/openspec/changes/archive/2026-05-11-vth-workflow-templates/design.md similarity index 100% rename from openspec/changes/vth-workflow-templates/design.md rename to openspec/changes/archive/2026-05-11-vth-workflow-templates/design.md diff --git a/openspec/changes/vth-workflow-templates/proposal.md b/openspec/changes/archive/2026-05-11-vth-workflow-templates/proposal.md similarity index 100% rename from openspec/changes/vth-workflow-templates/proposal.md rename to openspec/changes/archive/2026-05-11-vth-workflow-templates/proposal.md diff --git a/openspec/changes/vth-workflow-templates/specs/vth-workflow-templates/spec.md b/openspec/changes/archive/2026-05-11-vth-workflow-templates/specs/vth-workflow-templates/spec.md similarity index 100% rename from openspec/changes/vth-workflow-templates/specs/vth-workflow-templates/spec.md rename to openspec/changes/archive/2026-05-11-vth-workflow-templates/specs/vth-workflow-templates/spec.md diff --git a/openspec/changes/vth-workflow-templates/tasks.md b/openspec/changes/archive/2026-05-11-vth-workflow-templates/tasks.md similarity index 100% rename from openspec/changes/vth-workflow-templates/tasks.md rename to openspec/changes/archive/2026-05-11-vth-workflow-templates/tasks.md diff --git a/openspec/changes/archive/2026-05-11-wms-wfs-layers/builds/build.json b/openspec/changes/archive/2026-05-11-wms-wfs-layers/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/archive/2026-05-11-wms-wfs-layers/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/wms-wfs-layers/design.md b/openspec/changes/archive/2026-05-11-wms-wfs-layers/design.md similarity index 100% rename from openspec/changes/wms-wfs-layers/design.md rename to openspec/changes/archive/2026-05-11-wms-wfs-layers/design.md diff --git a/openspec/changes/wms-wfs-layers/proposal.md b/openspec/changes/archive/2026-05-11-wms-wfs-layers/proposal.md similarity index 100% rename from openspec/changes/wms-wfs-layers/proposal.md rename to openspec/changes/archive/2026-05-11-wms-wfs-layers/proposal.md diff --git a/openspec/changes/wms-wfs-layers/specs/wms-wfs-layers/spec.md b/openspec/changes/archive/2026-05-11-wms-wfs-layers/specs/wms-wfs-layers/spec.md similarity index 100% rename from openspec/changes/wms-wfs-layers/specs/wms-wfs-layers/spec.md rename to openspec/changes/archive/2026-05-11-wms-wfs-layers/specs/wms-wfs-layers/spec.md diff --git a/openspec/changes/wms-wfs-layers/tasks.md b/openspec/changes/archive/2026-05-11-wms-wfs-layers/tasks.md similarity index 100% rename from openspec/changes/wms-wfs-layers/tasks.md rename to openspec/changes/archive/2026-05-11-wms-wfs-layers/tasks.md diff --git a/openspec/changes/base-register-seed-data/builds/build.json b/openspec/changes/base-register-seed-data/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/base-register-seed-data/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/case-definition-portability/builds/build.json b/openspec/changes/case-definition-portability/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/case-definition-portability/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/case-sharing-collaboration/builds/build.json b/openspec/changes/case-sharing-collaboration/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/case-sharing-collaboration/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/legesberekening/builds/build.json b/openspec/changes/legesberekening/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/legesberekening/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/mijn-overheid-integration/builds/build.json b/openspec/changes/mijn-overheid-integration/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/mijn-overheid-integration/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/mobiel-inspectie/builds/build.json b/openspec/changes/mobiel-inspectie/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/mobiel-inspectie/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/multi-tenant-saas/builds/build.json b/openspec/changes/multi-tenant-saas/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/multi-tenant-saas/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/parafering-actions/builds/build.json b/openspec/changes/parafering-actions/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/parafering-actions/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/parafering-dashboard/builds/build.json b/openspec/changes/parafering-dashboard/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/parafering-dashboard/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/pipelinq-app-scaffold/.openspec.yaml b/openspec/changes/pipelinq-app-scaffold/.openspec.yaml deleted file mode 100644 index 07c123ae..00000000 --- a/openspec/changes/pipelinq-app-scaffold/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -status: proposed -created: "2026-03-20" diff --git a/openspec/changes/pipelinq-app-scaffold/proposal.md b/openspec/changes/pipelinq-app-scaffold/proposal.md deleted file mode 100644 index 89597d57..00000000 --- a/openspec/changes/pipelinq-app-scaffold/proposal.md +++ /dev/null @@ -1,15 +0,0 @@ -## Why - -pipelinq-app-scaffold Specification is not yet implemented in Procest. This change proposes adding this capability based on the detailed spec in `specs/pipelinq-app-scaffold/spec.md`. - - - -## What Changes - -See `specs/pipelinq-app-scaffold/spec.md` for full requirements and scenarios. - -## Impact - -- **Code**: New frontend views and/or backend services -- **Dependencies**: OpenRegister (data storage), Nextcloud platform APIs -- **Testing**: Unit tests for new services, integration tests for API endpoints diff --git a/openspec/changes/pipelinq-app-scaffold/specs/pipelinq-app-scaffold/spec.md b/openspec/changes/pipelinq-app-scaffold/specs/pipelinq-app-scaffold/spec.md deleted file mode 100644 index 0307b26f..00000000 --- a/openspec/changes/pipelinq-app-scaffold/specs/pipelinq-app-scaffold/spec.md +++ /dev/null @@ -1,313 +0,0 @@ ---- -status: implemented ---- -# pipelinq-app-scaffold Specification - -## Purpose -Define the Nextcloud app scaffolding, build system, translation setup, and admin settings for the Pipelinq client and request management app. Mirrors the Procest scaffold with its own app identity, routing, component registration, and OpenRegister integration. - -## Context -Pipelinq is a CRM and client management app for Nextcloud, serving as the sister app to Procest (case management). It follows the same architectural patterns: thin client with no own database tables, Vue 2 frontend with Pinia state management, and all data stored in OpenRegister. The app scaffold must provide the foundational structure that all Pipelinq features build upon, including proper Nextcloud integration, build tooling, translation support, and admin settings for register/schema configuration. - -## Requirements - -### Requirement 1: App MUST be a valid Nextcloud app with proper metadata -The Pipelinq app MUST be installable as a standard Nextcloud app with proper `info.xml` metadata, PHP namespace, and dependency declarations. - -#### Scenario 1.1: App registration in Nextcloud app list -- GIVEN the Pipelinq app directory exists in `apps-extra/pipelinq/` -- WHEN Nextcloud scans for available apps -- THEN the app MUST appear in the apps list with id `pipelinq`, name "Pipelinq", and namespace `OCA\Pipelinq` -- AND `info.xml` MUST declare compatibility with Nextcloud versions 28 through 33 -- AND `info.xml` MUST declare PHP 8.1+ as minimum requirement - -#### Scenario 1.2: App enable with OpenRegister dependency -- GIVEN Nextcloud is running and OpenRegister is installed and enabled -- WHEN an admin enables the Pipelinq app via `php occ app:enable pipelinq` -- THEN the app MUST activate without errors -- AND it MUST register a navigation entry in the top bar with icon and translated name -- AND the repair step MUST run to create/detect the Pipelinq register in OpenRegister - -#### Scenario 1.3: App enable without OpenRegister -- GIVEN Nextcloud is running but OpenRegister is NOT installed -- WHEN a user navigates to `/apps/pipelinq/` -- THEN the app MUST display an `NcEmptyContent` component explaining that OpenRegister is required -- AND an "Install OpenRegister" button MUST link to the Nextcloud app store - -#### Scenario 1.4: App categories and description -- GIVEN the `info.xml` file -- WHEN the Nextcloud app store reads the metadata -- THEN the app MUST be categorized under "organization" and "social" -- AND the description MUST be provided in both English and Dutch - -#### Scenario 1.5: License declaration -- GIVEN the app source code -- WHEN checking license headers -- THEN all PHP files MUST include EUPL-1.2 license headers -- AND `info.xml` MUST declare `agpl` as the license for Nextcloud compatibility - -### Requirement 2: App MUST provide a single-page application entry point -The app MUST serve a Vue 2 SPA from a DashboardController that mounts to the `#content` element. - -#### Scenario 2.1: Dashboard page load -- GIVEN the app is enabled and a user is logged in -- WHEN the user navigates to `/apps/pipelinq/` -- THEN the DashboardController MUST return a `TemplateResponse` with template name `main` -- AND the page MUST load the `pipelinq-main.js` webpack bundle via `Util::addScript()` -- AND the Vue app MUST initialize with `PiniaVuePlugin` and mount to `#content` - -#### Scenario 2.2: Vue app initialization sequence -- GIVEN the `src/main.js` entry point -- WHEN the script executes -- THEN it MUST register `PiniaVuePlugin` with Vue before creating the app instance -- AND it MUST create the Vue instance with Pinia and Vue Router -- AND it MUST call `$mount('#content')` before `initializeStores()` -- AND `initializeStores()` MUST fetch settings and register all object types - -#### Scenario 2.3: App shell with NcContent -- GIVEN the root `App.vue` component -- WHEN it renders -- THEN it MUST use `NcContent` with `app-name="pipelinq"` -- AND it MUST show a loading state while stores initialize (`NcLoadingIcon`) -- AND it MUST check `hasOpenRegisters` before rendering the main content -- AND it MUST include the `MainMenu` navigation component and `router-view` for page content - -#### Scenario 2.4: Shared library CSS import -- GIVEN the main entry point -- WHEN the app bundles are built -- THEN `main.js` MUST explicitly import `@conduction/nextcloud-vue/css/index.css` -- AND this import MUST appear before any component imports to ensure correct CSS cascade - -#### Scenario 2.5: Global translation mixin -- GIVEN any Vue component in the app -- WHEN the component needs to display translated text -- THEN `main.js` MUST register a global mixin providing `t()` and `n()` methods -- AND these MUST use `@nextcloud/l10n` with app id `pipelinq` - -### Requirement 3: Vue Router MUST define all application routes -The app MUST use Vue Router in history mode with routes for all primary views. - -#### Scenario 3.1: Route definitions -- GIVEN the router at `src/router/index.js` -- WHEN the app initializes -- THEN the router MUST use history mode with base URL `generateUrl('/apps/pipelinq')` -- AND it MUST define routes for: Dashboard (`/`), Clients (`/clients`), ClientDetail (`/clients/:id`), Requests (`/requests`), RequestDetail (`/requests/:id`), Settings (`/settings`) -- AND it MUST include a catch-all route (`*`) that redirects to `/` - -#### Scenario 3.2: Route props for detail views -- GIVEN a detail view route like `/clients/:id` -- WHEN the route is matched -- THEN the route MUST pass the `id` param as a prop to the component (e.g., `props: route => ({ clientId: route.params.id })`) - -#### Scenario 3.3: Navigation guard for settings -- GIVEN a non-admin user -- WHEN they attempt to navigate to `/settings` -- THEN the settings view MUST check `settingsStore.getIsAdmin` and display an access denied message if false - -### Requirement 4: App MUST use webpack build system extending Nextcloud base config -The build system MUST extend `@nextcloud/webpack-vue-config` with entry points for the main SPA and admin settings. - -#### Scenario 4.1: Build produces correct bundles -- GIVEN the source files exist in `src/` -- WHEN `npm run build` is executed -- THEN it MUST produce `js/pipelinq-main.js` for the dashboard SPA -- AND it MUST produce `js/pipelinq-settings.js` for the admin settings page -- AND both bundles MUST be minified for production builds - -#### Scenario 4.2: Webpack alias for shared library deduplication -- GIVEN `@conduction/nextcloud-vue` is a dependency -- WHEN webpack resolves imports -- THEN `webpack.config.js` MUST configure resolve aliases to deduplicate Vue, Pinia, and `@nextcloud/vue` between the app and the shared library - -#### Scenario 4.3: Development mode with hot reload -- GIVEN the developer runs `npm run dev` -- WHEN source files are modified -- THEN webpack MUST rebuild the affected bundles -- AND the `--watch` flag MUST be available for continuous rebuilds - -#### Scenario 4.4: Source map generation -- GIVEN a development build -- WHEN `npm run dev` is executed -- THEN source maps MUST be generated for debugging -- AND production builds MUST NOT include source maps - -### Requirement 5: App MUST support multilingual translations (EN/NL minimum) -All user-facing strings MUST be wrapped in translation functions with English as the primary language and Dutch as a required secondary language. - -#### Scenario 5.1: English translation rendering -- GIVEN a user with English (`en`) locale -- WHEN viewing the Pipelinq app -- THEN all UI text MUST be displayed in English -- AND navigation items, form labels, button text, error messages, and empty states MUST all be translated - -#### Scenario 5.2: Dutch translation rendering -- GIVEN a user with Dutch (`nl`) locale -- WHEN viewing the Pipelinq app -- THEN all UI text MUST be displayed in Dutch -- AND the navigation MUST show "Klanten" instead of "Clients", "Verzoeken" instead of "Requests" - -#### Scenario 5.3: Translation function usage in Vue templates -- GIVEN any Vue component with user-facing text -- WHEN the component renders -- THEN all strings MUST use `t('pipelinq', 'key')` in templates or `this.t('pipelinq', 'key')` in script -- AND plural strings MUST use `n('pipelinq', 'singular', 'plural', count)` - -#### Scenario 5.4: Translation function usage in PHP -- GIVEN any PHP controller or service that returns user-facing messages -- WHEN the code constructs a response message -- THEN it MUST use `$this->l->t('key')` with the IL10N service injected via constructor - -#### Scenario 5.5: Translation file structure -- GIVEN the `translationfiles/` directory -- WHEN translations are generated -- THEN `translationfiles/en/` MUST contain the source language strings -- AND `translationfiles/nl/` MUST contain Dutch translations -- AND the translation extraction tool (`translationtool.phar`) MUST be runnable without errors - -### Requirement 6: App MUST provide admin settings page -The app MUST register an admin settings section for register/schema configuration and app preferences. - -#### Scenario 6.1: Admin settings section registration -- GIVEN the `info.xml` file -- WHEN Nextcloud loads admin settings -- THEN `Sections\SettingsSection` MUST be registered as an admin settings section -- AND `Settings\AdminSettings` MUST be registered as the admin settings page -- AND the section MUST appear under "Administration" in the settings sidebar - -#### Scenario 6.2: Settings page access and rendering -- GIVEN an admin user -- WHEN navigating to `/settings/admin/pipelinq` -- THEN the admin settings page MUST load with the `pipelinq-settings.js` bundle -- AND it MUST display configuration for register/schema mappings (register ID, client schema, request schema, contact schema) -- AND it MUST include a "Reload configuration" button to re-import from `pipelinq_register.json` - -#### Scenario 6.3: Settings page restricted to admins -- GIVEN a regular (non-admin) user -- WHEN attempting to access `/settings/admin/pipelinq` -- THEN Nextcloud MUST deny access based on the `AdminSettings` class's admin-only priority - -#### Scenario 6.4: In-app settings route -- GIVEN a user navigates to `/apps/pipelinq/settings` within the SPA -- WHEN the settings component renders -- THEN it MUST display the same configuration options as the admin settings page -- AND it MUST call the `/api/settings` endpoint for reading and writing configuration -- AND it MUST only be accessible to admin users (checked via `settingsStore.getIsAdmin`) - -### Requirement 7: App MUST have a repair step for register initialization -The app MUST automatically create or detect the Pipelinq register and schemas in OpenRegister during installation or upgrade. - -#### Scenario 7.1: Repair step execution on install -- GIVEN the app is being enabled for the first time -- WHEN the `InitializeSettings` repair step runs -- THEN it MUST call `SettingsService::loadConfiguration()` to import the register from `pipelinq_register.json` -- AND the import MUST create the register and all defined schemas in OpenRegister -- AND it MUST store the resulting register and schema IDs in `IAppConfig` - -#### Scenario 7.2: Repair step on upgrade with version check -- GIVEN the app is being upgraded from version 1.0.0 to 1.1.0 -- WHEN the repair step runs -- THEN `ConfigurationService::importFromApp()` MUST compare the version in `pipelinq_register.json` to the previously imported version -- AND if the version is newer, schemas MUST be updated without losing existing data -- AND if the version is the same, the import MUST be skipped (unless forced) - -#### Scenario 7.3: Register configuration JSON structure -- GIVEN the file `lib/Settings/pipelinq_register.json` -- WHEN the register is imported -- THEN the JSON MUST follow OpenAPI 3.0.0 format with `info.title`, `info.version`, and schema definitions under `components.schemas` -- AND each schema MUST include Schema.org type annotations in `x-schema-org-type` - -### Requirement 8: App MUST have a GitHub repository -The app source code MUST be hosted at `ConductionNL/pipelinq` on GitHub with proper CI/CD. - -#### Scenario 8.1: Repository exists and is public -- GIVEN the ConductionNL GitHub organization -- WHEN checking for the pipelinq repository -- THEN `https://github.com/ConductionNL/pipelinq` MUST exist and be public -- AND the repository MUST have a `main` branch as default - -#### Scenario 8.2: Repository contains required files -- GIVEN the repository root -- WHEN listing the contents -- THEN it MUST contain: `appinfo/info.xml`, `appinfo/routes.php`, `lib/`, `src/`, `webpack.config.js`, `package.json`, `composer.json` - -#### Scenario 8.3: CI workflow runs linting -- GIVEN a pull request is opened -- WHEN the CI workflow runs -- THEN it MUST execute `composer check:strict` (PHPCS, PHPMD, Psalm, PHPStan) -- AND it MUST execute `npm run lint` for ESLint - -### Requirement 9: Navigation menu MUST show primary entity sections -The app navigation MUST include menu items for all primary views using `NcAppNavigation` components. - -#### Scenario 9.1: Navigation rendering with icons -- GIVEN the user opens the Pipelinq app -- WHEN the `MainMenu.vue` component renders -- THEN the menu MUST include items for: Dashboard (with dashboard icon), Clients (with contacts icon), Requests (with inbox icon), and Documentation (external link) -- AND the footer MUST include a settings/configuration item - -#### Scenario 9.2: Active route highlighting -- GIVEN the user is on the Clients list page -- WHEN the navigation renders -- THEN the "Clients" menu item MUST be highlighted as active -- AND all other items MUST be in their default state - -#### Scenario 9.3: Navigation item count badges -- GIVEN there are unprocessed requests requiring attention -- WHEN the navigation renders -- THEN the "Requests" menu item MAY display a count badge (enterprise feature) - -### Requirement 10: App MUST integrate with Nextcloud Dashboard widgets -The app SHALL provide dashboard widgets for the Nextcloud Dashboard, showing key metrics. - -#### Scenario 10.1: Widget registration in info.xml -- GIVEN the `info.xml` configuration -- WHEN the app registers its features -- THEN dashboard widgets MUST be declared as separate webpack entry points -- AND each widget MUST have a corresponding PHP class implementing `IWidget` - -#### Scenario 10.2: Widget renders independently -- GIVEN the Nextcloud Dashboard page -- WHEN the user adds a Pipelinq widget -- THEN the widget MUST load its own JavaScript bundle (not the full SPA) -- AND it MUST fetch data independently using the object store pattern - -#### Scenario 10.3: Widget displays client/request summary -- GIVEN the widget is rendered on the Nextcloud Dashboard -- WHEN it loads -- THEN it MUST display a summary of recent clients or open requests -- AND each item MUST link to the corresponding detail view in the Pipelinq app - ---- - -## Current Implementation Status - -**Fully implemented.** The Pipelinq app scaffold is complete and functional. - -**Implemented (with file paths -- in the `pipelinq/` submodule):** -- **App registration**: `pipelinq/appinfo/info.xml` -- id `pipelinq`, namespace `Pipelinq`, Nextcloud 28-33 compatibility, PHP 8.1+ requirement. -- **Navigation entry**: Registered in `info.xml` as a top-bar navigation item. -- **SPA entry point**: `pipelinq/src/main.js` -- Vue 2 app with Pinia, mounts to `#content`. -- **Webpack build**: `pipelinq/webpack.config.js` -- extends `@nextcloud/webpack-vue-config` with entry points for `pipelinq-main.js` and `pipelinq-settings.js`. -- **Admin settings**: `pipelinq/lib/Settings/AdminSettings.php` with section registration and settings Vue component. -- **Repair step**: `pipelinq/lib/Repair/InitializeSettings.php` for register/schema initialization. -- **Register config**: `pipelinq/lib/Settings/pipelinq_register.json` -- defines the Pipelinq register and schemas. -- **Settings store**: `pipelinq/src/store/modules/settings.js` for config fetching. -- **Object store**: `pipelinq/src/store/modules/object.js` -- uses `createObjectStore('object')` from shared library. -- **Translation support**: `t('pipelinq', ...)` used throughout Vue components. -- **GitHub repository**: https://github.com/ConductionNL/pipelinq exists. - -**All core scaffold requirements are implemented.** - -## Standards & References - -- **Nextcloud App Development Guidelines**: App structure follows Nextcloud conventions (info.xml, routes.php, AppFramework controllers, admin settings sections). -- **Vue 2 + Pinia**: Standard frontend stack for Conduction apps, with `PiniaVuePlugin` for Vue 2 compatibility. -- **@nextcloud/webpack-vue-config**: Nextcloud's standard webpack configuration extended with custom entry points. -- **@conduction/nextcloud-vue**: Shared component library providing `createObjectStore`, `CnIndexPage`, `CnDetailPage`, etc. -- **Nextcloud L10N**: Translation functions `t()` and `n()` used per Nextcloud conventions. -- **EUPL-1.2**: License declared in PHP file headers. -- **OpenAPI 3.0.0**: Register configuration format for `pipelinq_register.json`. - -## Specificity Assessment - -This spec is fully implementable and already implemented. All 10 requirements have comprehensive scenarios covering app registration, SPA entry, routing, build system, translations, admin settings, repair steps, repository, navigation, and widgets. The spec accurately reflects the Conduction app architecture pattern shared across Procest, Pipelinq, Softwarecatalog, and other apps. diff --git a/openspec/changes/pipelinq-client-management/.openspec.yaml b/openspec/changes/pipelinq-client-management/.openspec.yaml deleted file mode 100644 index 07c123ae..00000000 --- a/openspec/changes/pipelinq-client-management/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -status: proposed -created: "2026-03-20" diff --git a/openspec/changes/pipelinq-client-management/proposal.md b/openspec/changes/pipelinq-client-management/proposal.md deleted file mode 100644 index 5e7731a6..00000000 --- a/openspec/changes/pipelinq-client-management/proposal.md +++ /dev/null @@ -1,15 +0,0 @@ -## Why - -pipelinq-client-management Specification is not yet implemented in Procest. This change proposes adding this capability based on the detailed spec in `specs/pipelinq-client-management/spec.md`. - -**Feature tiers:** - -## What Changes - -See `specs/pipelinq-client-management/spec.md` for full requirements and scenarios. - -## Impact - -- **Code**: New frontend views and/or backend services -- **Dependencies**: OpenRegister (data storage), Nextcloud platform APIs -- **Testing**: Unit tests for new services, integration tests for API endpoints diff --git a/openspec/changes/pipelinq-client-management/specs/pipelinq-client-management/spec.md b/openspec/changes/pipelinq-client-management/specs/pipelinq-client-management/spec.md deleted file mode 100644 index 96243ece..00000000 --- a/openspec/changes/pipelinq-client-management/specs/pipelinq-client-management/spec.md +++ /dev/null @@ -1,466 +0,0 @@ ---- -status: implemented ---- -# pipelinq-client-management Specification - -## Purpose -Define the client and request management domain for Pipelinq: clients, requests (verzoeken), and contacts. All entities are stored in OpenRegister under the Pipelinq register. Requests represent the pre-state of a case -- a yet-to-be-determined or incoming case before it enters formal case management in Procest. The client entity links organizations and persons across both Pipelinq (CRM) and Procest (case management) contexts. - -## Context -Pipelinq serves as the CRM front door for Dutch municipalities and organizations using Nextcloud. Citizens, businesses, and other parties first appear as clients in Pipelinq, where their requests (verzoeken) are tracked. When a request matures into a formal case, it is converted into a Procest case with the client linked as a participant (betrokkene). This spec defines the data model, CRUD operations, and views for managing clients and requests, following the same thin-client architecture as Procest: all data in OpenRegister, Vue 2 frontend with Pinia stores, no backend CRUD controllers. The client entity maps to Schema.org `Person`/`Organization` and aligns with the ZGW Klantinteracties API standard and GEMMA KCC reference architecture. - -## Requirements - -### Requirement 1: Client-management schemas MUST be defined in the Pipelinq register -The Pipelinq register MUST include schemas for client, request, and contact entities, imported during app initialization. - -#### Scenario 1.1: Client schema definition -- GIVEN the `pipelinq_register.json` configuration -- WHEN the register is imported via `ConfigurationService::importFromApp()` -- THEN a `client` schema MUST be created with properties: name (string, required), type (enum: person/organization, required), email (string/email format), phone (string), address (object with street, postalCode, city, country), notes (string), kvkNumber (string, for organizations), bsn (string, for persons -- stored encrypted), website (string/url), tags (array of strings), createdAt (datetime, auto), updatedAt (datetime, auto) -- AND the schema MUST include `x-schema-org-type: schema:Person` for person type and `schema:Organization` for organization type - -#### Scenario 1.2: Request schema definition -- GIVEN the register configuration -- WHEN the register is imported -- THEN a `request` schema MUST be created with properties: title (string, required), description (string), client (string/reference to client, required), status (enum: new/in-progress/converted/closed, default: new), priority (enum: low/normal/high/urgent, default: normal), category (string), channel (enum: email/phone/web/counter/letter), requestedAt (datetime), convertedCaseId (string, reference to Procest case after conversion), assignee (string), notes (string), attachments (array of file references), activity (array of activity entries) - -#### Scenario 1.3: Contact schema definition -- GIVEN the register configuration -- WHEN the register is imported -- THEN a `contact` schema MUST be created with properties: client (string/reference to client, required), name (string, required), role (string, e.g., "decision maker", "technical contact"), email (string/email), phone (string), isPrimary (boolean, default: false), notes (string) -- AND the schema MUST include `x-schema-org-type: schema:ContactPoint` - -#### Scenario 1.4: Schema auto-configuration stores IDs -- GIVEN the schemas are imported -- WHEN `SettingsService::autoConfigureAfterImport()` runs -- THEN schema IDs for `client`, `request`, and `contact` MUST be stored in `IAppConfig` under keys `client_schema`, `request_schema`, `contact_schema` -- AND the object store MUST register these types during `initializeStores()` - -#### Scenario 1.5: Existing register detection on re-enable -- GIVEN the Pipelinq register and schemas already exist from a previous installation -- WHEN the app is re-enabled -- THEN the repair step MUST detect existing schemas by slug -- AND it MUST NOT create duplicates -- AND it MUST update schema IDs in config if they have changed - -### Requirement 2: Client list view MUST display paginated, searchable client overview -The frontend MUST display a list of clients with search, sort, filter, and sidebar capabilities using the shared library's `CnIndexPage` component. - -#### Scenario 2.1: Client list rendering with CnIndexPage -- GIVEN the user navigates to `/apps/pipelinq/clients` -- WHEN `ClientList.vue` mounts and `useListView('client')` initializes -- THEN the composable MUST fetch the client schema and initial collection from OpenRegister -- AND `CnIndexPage` MUST render a data table with columns for: name, type (person/organization), email, phone, tags -- AND the list MUST support pagination via `@page-changed` - -#### Scenario 2.2: Client search -- GIVEN the client list is displayed -- WHEN the user types "Gemeente Amsterdam" in the sidebar search -- THEN `fetchCollection('client', { _search: 'Gemeente Amsterdam' })` MUST be called -- AND results MUST update to show only matching clients - -#### Scenario 2.3: Filter clients by type -- GIVEN the client list is displayed -- WHEN the user filters by type "organization" via the sidebar filter -- THEN `fetchCollection('client', { '_filters[type]': 'organization' })` MUST be called -- AND only organization-type clients MUST be shown - -#### Scenario 2.4: Client row click navigates to detail -- GIVEN a client row in the list -- WHEN the user clicks the row -- THEN the router MUST navigate to `/apps/pipelinq/clients/:id` with the client's ID - -#### Scenario 2.5: Create client from list -- GIVEN the user is on the client list -- WHEN the user clicks the "+" add button (CnIndexPage `@add` event) -- THEN a `ClientCreateDialog.vue` MUST open -- AND the dialog MUST include fields for: name (required), type (person/organization, required), email, phone, address fields, notes - -### Requirement 3: Client detail view MUST display full client information with related data -The frontend MUST provide a comprehensive client detail view showing client information, related contacts, linked requests, and linked cases. - -#### Scenario 3.1: Client detail page load -- GIVEN the user navigates to `/apps/pipelinq/clients/:id` -- WHEN `ClientDetail.vue` mounts -- THEN it MUST fetch the client via `fetchObject('client', clientId)` -- AND it MUST fetch contacts via `fetchCollection('contact', { '_filters[client]': clientId })` -- AND it MUST fetch requests via `fetchCollection('request', { '_filters[client]': clientId })` - -#### Scenario 3.2: Client information card with editing -- GIVEN the client data is loaded -- WHEN the detail view renders -- THEN it MUST use `CnDetailPage` with `CnDetailCard` sections -- AND the client information card MUST show editable fields for: name, type, email, phone, address (street, postalCode, city, country), kvkNumber (if organization), website, notes, tags -- AND a Save button MUST persist changes via `saveObject('client', updatedData)` - -#### Scenario 3.3: Contacts section -- GIVEN a client with 3 contacts -- WHEN the Contacts card renders -- THEN each contact MUST display: name, role, email, phone, isPrimary badge -- AND an "Add contact" button MUST open a dialog for creating a new contact linked to this client -- AND each contact MUST have edit and delete actions - -#### Scenario 3.4: Requests section -- GIVEN a client with 5 requests -- WHEN the Requests card renders -- THEN each request MUST display: title, status (badge), priority (badge), channel, requestedAt date -- AND clicking a request MUST navigate to the request detail view -- AND a "New request" button MUST open the request create form with the client pre-selected - -#### Scenario 3.5: Linked Procest cases (cross-app) -- GIVEN a client whose requests have been converted to Procest cases (convertedCaseId is populated) -- WHEN the client detail renders a "Cases" section -- THEN it MUST display each linked case with title, status, and identifier -- AND clicking a case link MUST navigate to `/apps/procest/cases/:caseId` (cross-app deep link) -- AND if Procest is not installed, the cases section MUST show a note explaining this - -### Requirement 4: Client CRUD operations MUST work through OpenRegister -The frontend MUST support creating, editing, and deleting clients via the object store. - -#### Scenario 4.1: Create person client -- GIVEN the user opens the client create dialog -- WHEN they fill in name "Jan de Vries", type "person", email "jan@example.nl", phone "06-12345678" -- THEN `saveObject('client', clientData)` MUST POST to OpenRegister -- AND the response MUST include server-assigned `id` and timestamps -- AND the new client MUST appear in the client list - -#### Scenario 4.2: Create organization client with KVK number -- GIVEN the user creates an organization client -- WHEN they fill in name "Gemeente Amsterdam", type "organization", kvkNumber "12345678" -- THEN the client object MUST be saved with the kvkNumber field -- AND the kvkNumber MUST be validated as an 8-digit string - -#### Scenario 4.3: Update client information -- GIVEN a client exists with ID `uuid-456` -- WHEN the user modifies the email and phone and saves -- THEN `saveObject('client', { id: 'uuid-456', ...updatedData })` MUST PUT to OpenRegister -- AND `updatedAt` MUST be refreshed server-side - -#### Scenario 4.4: Delete client with dependency check -- GIVEN a client with 3 linked requests and 2 contacts -- WHEN the user clicks delete -- THEN a confirmation dialog MUST warn "This client has 3 requests and 2 contacts. Are you sure?" -- AND on confirm, `deleteObject('client', clientId)` MUST DELETE from OpenRegister -- AND linked contacts SHOULD be cascade-deleted (or orphaned with a warning) -- AND linked requests MUST NOT be deleted (they retain the client reference for audit) - -#### Scenario 4.5: Validation on client create -- GIVEN the user attempts to create a client without a name -- WHEN validation runs -- THEN the name field MUST show an error "Name is required" -- AND the form MUST NOT submit -- AND the type field MUST also show an error if not selected - -### Requirement 5: Request list view MUST display paginated, searchable request overview -The frontend MUST display a list of requests with search, sort, filter, and status indicators. - -#### Scenario 5.1: Request list rendering -- GIVEN the user navigates to `/apps/pipelinq/requests` -- WHEN `RequestList.vue` mounts and `useListView('request')` initializes -- THEN the list MUST display columns: title, client name (resolved), status (badge with color), priority (badge), channel, requestedAt -- AND the list MUST support pagination and search - -#### Scenario 5.2: Request status badges -- GIVEN requests with different statuses -- WHEN the status column renders -- THEN "new" MUST display a blue badge -- AND "in-progress" MUST display an orange badge -- AND "converted" MUST display a green badge with link to the case -- AND "closed" MUST display a gray badge - -#### Scenario 5.3: Filter by status -- GIVEN the request list is displayed -- WHEN the user filters by status "new" -- THEN only new requests MUST be shown -- AND the filter MUST use `fetchCollection('request', { '_filters[status]': 'new' })` - -#### Scenario 5.4: Filter by client -- GIVEN the request list -- WHEN the user filters by a specific client -- THEN only requests for that client MUST be shown -- AND the filter MUST use `_filters[client]` parameter - -#### Scenario 5.5: Sort by priority and date -- GIVEN the request list -- WHEN the user sorts by priority descending -- THEN urgent requests MUST appear first, followed by high, normal, low -- AND within the same priority, newer requests MUST appear first (by requestedAt) - -### Requirement 6: Request detail view MUST show full request information -The frontend MUST provide a request detail view with client link, status management, and conversion to case. - -#### Scenario 6.1: Request detail page load -- GIVEN the user navigates to `/apps/pipelinq/requests/:id` -- WHEN `RequestDetail.vue` mounts -- THEN it MUST fetch the request via `fetchObject('request', requestId)` -- AND it MUST resolve the client reference to display client name and link - -#### Scenario 6.2: Request information editing -- GIVEN the request is not in "converted" or "closed" status -- WHEN the detail view renders -- THEN it MUST show editable fields for: title, description, priority, category, channel, assignee, notes -- AND it MUST show read-only fields for: client (with link to client detail), status, requestedAt, convertedCaseId - -#### Scenario 6.3: Request status transitions -- GIVEN a request with status "new" -- WHEN the user changes the status -- THEN the allowed transitions MUST be: new -> in-progress, new -> closed -- AND from "in-progress": in-progress -> converted (triggers case creation), in-progress -> closed -- AND "converted" and "closed" MUST be terminal states (no further transitions) - -#### Scenario 6.4: Request activity timeline -- GIVEN a request with activity entries -- WHEN the activity section renders -- THEN it MUST display events chronologically: creation, status changes, notes, field updates -- AND adding a note MUST push to the request's activity array and save - -#### Scenario 6.5: Request attachments -- GIVEN a request with file attachments -- WHEN the attachments section renders -- THEN each attachment MUST display filename, size, and download link -- AND the user MUST be able to upload new attachments via the files plugin -- AND attachments MUST be stored in OpenRegister's file storage for the request object - -### Requirement 7: Request-to-case conversion MUST bridge Pipelinq and Procest -The system MUST support converting a Pipelinq request into a Procest case, linking the client as a participant. - -#### Scenario 7.1: Convert request to case button -- GIVEN a request with status "in-progress" -- WHEN the detail view renders -- THEN a "Convert to case" button MUST be visible -- AND the button MUST be disabled if Procest is not installed - -#### Scenario 7.2: Conversion dialog -- GIVEN the user clicks "Convert to case" -- WHEN the conversion dialog opens -- THEN it MUST allow selecting a Procest case type from available types (fetched from Procest's settings or cross-app API) -- AND it MUST pre-fill the case title from the request title -- AND it MUST display a summary of what will be created - -#### Scenario 7.3: Case creation from request -- GIVEN the user confirms the conversion with a selected case type -- WHEN the conversion executes -- THEN a new case MUST be created in Procest's register via OpenRegister (using Procest's register/schema IDs) -- AND the case MUST include: title (from request), description (from request), caseType (selected), startDate (now), identifier (generated), status (initial for case type) -- AND a participant (role) object MUST be created in Procest linking the client as "initiator" - -#### Scenario 7.4: Request updated after conversion -- GIVEN the case is successfully created -- WHEN the conversion completes -- THEN the request's `status` MUST be set to "converted" -- AND `convertedCaseId` MUST be set to the new case's ID -- AND the request's activity MUST include a "converted_to_case" entry with the case identifier - -#### Scenario 7.5: Conversion failure rollback -- GIVEN the case creation fails (e.g., OpenRegister error) -- WHEN the conversion encounters an error -- THEN the request's status MUST NOT change (remain "in-progress") -- AND an error message MUST be displayed to the user -- AND no partial data (orphaned case or role) MUST remain in OpenRegister - -### Requirement 8: Contact management MUST support multiple contacts per client -The frontend MUST support CRUD operations on contacts linked to a client. - -#### Scenario 8.1: Contact list within client detail -- GIVEN a client with 4 contacts -- WHEN the Contacts card renders in ClientDetail -- THEN each contact MUST display: name, role, email, phone -- AND the primary contact MUST have a "Primary" badge -- AND contacts MUST be sorted with primary first, then alphabetically - -#### Scenario 8.2: Create contact -- GIVEN the user clicks "Add contact" on a client detail -- WHEN the contact create dialog opens -- THEN it MUST include fields for: name (required), role, email, phone, isPrimary (checkbox) -- AND saving MUST call `saveObject('contact', { client: clientId, ...contactData })` - -#### Scenario 8.3: Set primary contact -- GIVEN a client with 3 contacts, one marked as primary -- WHEN the user marks a different contact as primary -- THEN the old primary contact MUST have `isPrimary` set to `false` -- AND the new contact MUST have `isPrimary` set to `true` -- AND both updates MUST be saved to OpenRegister - -#### Scenario 8.4: Edit contact -- GIVEN an existing contact -- WHEN the user edits the role and phone number -- THEN `saveObject('contact', { id: contactId, ...updatedData })` MUST PUT to OpenRegister - -#### Scenario 8.5: Delete contact -- GIVEN a contact that is NOT the primary contact -- WHEN the user deletes the contact -- THEN `deleteObject('contact', contactId)` MUST remove it from OpenRegister -- AND the contact MUST disappear from the client detail's contact list -- AND if it IS the primary contact, a warning MUST appear: "This is the primary contact. Please set another contact as primary first." - -### Requirement 9: Navigation MUST include clients and requests menu items -The app navigation MUST show menu items for all primary entity types. - -#### Scenario 9.1: Navigation rendering with icons -- GIVEN the user opens the Pipelinq app -- WHEN `MainMenu.vue` renders within `NcAppNavigation` -- THEN the main list MUST include: Dashboard (dashboard icon), Clients (contacts/people icon), Requests (inbox/mail icon) -- AND a Documentation item MUST link externally - -#### Scenario 9.2: Footer navigation with settings -- GIVEN the navigation footer -- WHEN it renders -- THEN it MUST include a Configuration/Settings item routing to the settings view - -#### Scenario 9.3: Active route highlighting -- GIVEN the user is on the Clients list -- WHEN the navigation renders -- THEN the "Clients" menu item MUST be highlighted as active - -#### Scenario 9.4: Localized navigation labels -- GIVEN a user with Dutch locale -- WHEN the navigation renders -- THEN it MUST show "Klanten" for Clients, "Verzoeken" for Requests, "Dashboard" for Dashboard - -### Requirement 10: Client data MUST comply with privacy regulations -Client and contact data MUST be handled in compliance with AVG/GDPR, including personal data protection and access control. - -#### Scenario 10.1: BSN field encrypted storage -- GIVEN a person-type client with a BSN (Burgerservicenummer) -- WHEN the client is saved to OpenRegister -- THEN the BSN MUST be stored in an encrypted field (using OpenRegister's encryption support) -- AND the BSN MUST only be visible to users with appropriate permissions - -#### Scenario 10.2: Client data access restricted to authorized users -- GIVEN a regular user without CRM role -- WHEN they attempt to access client data -- THEN the system MUST enforce access control based on Nextcloud group membership or app-level permissions -- AND unauthorized users MUST receive a 403 response from the API - -#### Scenario 10.3: Data export capability -- GIVEN a client requests their data (AVG right of access) -- WHEN the admin exports the client's data -- THEN the export MUST include all stored fields, contacts, and request history -- AND the export MUST be available as JSON or PDF - -#### Scenario 10.4: Data deletion capability -- GIVEN a client requests data deletion (AVG right to erasure) -- WHEN the admin deletes the client -- THEN all personal data MUST be removed from OpenRegister -- AND contacts MUST be deleted -- AND request references MUST be anonymized (client field cleared, note added) - -#### Scenario 10.5: Audit trail for personal data access -- GIVEN a user views a client's detail page -- WHEN the client data is fetched from OpenRegister -- THEN the audit trail plugin MUST record the access event -- AND the audit log MUST include: user, timestamp, object type, object ID, action (view/edit/delete) - -### Requirement 11: Client deduplication MUST prevent duplicate entries -The system MUST provide mechanisms to detect and merge duplicate client records. - -#### Scenario 11.1: Duplicate detection on create -- GIVEN a user creates a new client with name "Gemeente Amsterdam" -- WHEN the create form is submitted -- THEN the system MUST check for existing clients with matching name (case-insensitive) -- AND if potential duplicates are found, a warning MUST be displayed: "Similar clients found: [list]. Continue creating or view existing?" - -#### Scenario 11.2: Duplicate detection by email -- GIVEN a user creates a client with email "info@amsterdam.nl" -- WHEN the create form is submitted -- THEN the system MUST check for existing clients with the same email -- AND if found, a warning MUST be displayed with a link to the existing client - -#### Scenario 11.3: Duplicate detection by KVK number -- GIVEN a user creates an organization client with kvkNumber "12345678" -- WHEN the create form is submitted -- THEN the system MUST check for existing organizations with the same KVK number -- AND if found, the system MUST prevent creation with error: "An organization with this KVK number already exists" - -#### Scenario 11.4: Merge duplicate clients (V1) -- GIVEN two client records for the same entity -- WHEN the admin selects both and clicks "Merge" -- THEN the system MUST present a merge dialog showing fields from both records -- AND the admin MUST choose which fields to keep for each conflicting field -- AND after merge, all requests and contacts from both records MUST be linked to the surviving record -- AND the duplicate record MUST be deleted - -### Requirement 12: Cross-app client resolution between Pipelinq and Procest -When a client appears in both Pipelinq and Procest (as a case participant), the system MUST provide cross-referencing capabilities. - -#### Scenario 12.1: Client profile shows Procest cases -- GIVEN a client in Pipelinq whose requests have been converted to Procest cases -- WHEN viewing the client detail -- THEN a "Cases" section MUST query Procest's register for cases where the client appears as a participant -- AND each case MUST show: identifier, title, status, case type -- AND the query MUST use OpenRegister cross-register filtering (filter by client ID in Procest role objects) - -#### Scenario 12.2: Procest case detail shows client from Pipelinq -- GIVEN a Procest case with a participant that references a Pipelinq client -- WHEN the case detail's ParticipantsSection renders -- THEN the participant's name MUST be resolved from the Pipelinq client object -- AND clicking the participant name MUST deep-link to `/apps/pipelinq/clients/:clientId` - -#### Scenario 12.3: Client not found in cross-app query -- GIVEN a case participant that references a client ID that no longer exists in Pipelinq -- WHEN the cross-app resolution attempts to fetch the client -- THEN it MUST handle the 404 gracefully -- AND display the raw participant name instead of a resolved link -- AND log a warning about the orphaned reference - ---- - -## Current Implementation Status - -**Not implemented in the current Pipelinq app.** The Pipelinq app exists as a submodule at `pipelinq/` but is focused on lead/prospect/pipeline management rather than client/request management as described in this spec. - -**Current Pipelinq entity model** (in `pipelinq/src/store/modules/`): -- `object.js` -- generic object store (same pattern as Procest) -- `settings.js` -- app settings -- `leadSources.js` -- lead source configuration -- `requestChannels.js` -- request channel configuration -- `product.js` -- product management -- `prospect.js` -- prospect/lead management - -**What exists as foundation:** -- The `createObjectStore('object')` pattern is in place and ready for new types -- `initializeStores()` in `store/store.js` registers types from settings config -- The repair step (`InitializeSettings.php`) and settings service are functional -- Navigation and router are configured and can be extended with new routes -- `CnIndexPage` and `CnDetailPage` from the shared library are available for building list/detail views - -**What needs to be built:** -- Client, request, and contact schema definitions in `pipelinq_register.json` -- `ClientList.vue`, `ClientDetail.vue`, `ClientCreateDialog.vue` -- `RequestList.vue`, `RequestDetail.vue` -- Contact management components within client detail -- Request-to-case conversion flow (cross-app bridge to Procest) -- Navigation items for Clients and Requests -- Privacy compliance features (BSN encryption, access control, data export/deletion) -- Duplicate detection and merge functionality -- Cross-app client resolution between Pipelinq and Procest - -## Standards & References - -- **ZGW Klantinteracties API (VNG)**: Client/contact management aligns with the Klantinteracties standard for Dutch government systems. -- **GEMMA KCC**: Klantcontactcentrum reference architecture -- Pipelinq serves as the KCC intake component. -- **Schema.org**: Clients map to `schema:Person` or `schema:Organization`, contacts to `schema:ContactPoint`, requests to `schema:Request`. -- **AVG/GDPR**: Client and contact personal data requires encryption (BSN), access control, data export, and deletion capabilities. -- **KVK (Kamer van Koophandel)**: Organization identification via 8-digit KVK number. -- **Common Ground**: Data layer in OpenRegister, CRM layer in Pipelinq, case layer in Procest. -- **CMMN 1.1**: Request-to-case conversion maps to the CMMN CaseFileItem creation pattern. -- **WCAG AA**: All client and request views must be accessible. -- **NL Design System**: CSS variables for government theming. - -## Specificity Assessment - -This spec is comprehensive with 12 requirements covering schemas, client CRUD, client detail with contacts/requests/cases, request CRUD, request-to-case conversion, contact management, navigation, privacy compliance, deduplication, and cross-app resolution. The spec defines both the Pipelinq-internal features and the critical cross-app bridge to Procest. - -**Key design decisions:** -- Client/request/contact schemas live in the Pipelinq register (not a separate register). -- Requests have a simple 4-state lifecycle (new -> in-progress -> converted/closed). -- Request-to-case conversion creates objects in Procest's register (cross-register write). -- BSN is stored encrypted, with access control enforcement. -- Duplicate detection uses name, email, and KVK number matching. -- Cross-app resolution queries OpenRegister across registers. - -**Feature tiers:** -- MVP: Client CRUD, Request CRUD, Navigation, Settings -- V1: Request-to-case conversion, Contact management, Cross-app resolution, Deduplication -- Enterprise: Privacy compliance (BSN encryption, data export/deletion, audit trails) diff --git a/openspec/changes/pipelinq-object-store/.openspec.yaml b/openspec/changes/pipelinq-object-store/.openspec.yaml deleted file mode 100644 index 07c123ae..00000000 --- a/openspec/changes/pipelinq-object-store/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -status: proposed -created: "2026-03-20" diff --git a/openspec/changes/pipelinq-object-store/proposal.md b/openspec/changes/pipelinq-object-store/proposal.md deleted file mode 100644 index f6eabdf5..00000000 --- a/openspec/changes/pipelinq-object-store/proposal.md +++ /dev/null @@ -1,15 +0,0 @@ -## Why - -pipelinq-object-store Specification is not yet implemented in Procest. This change proposes adding this capability based on the detailed spec in `specs/pipelinq-object-store/spec.md`. - - - -## What Changes - -See `specs/pipelinq-object-store/spec.md` for full requirements and scenarios. - -## Impact - -- **Code**: New frontend views and/or backend services -- **Dependencies**: OpenRegister (data storage), Nextcloud platform APIs -- **Testing**: Unit tests for new services, integration tests for API endpoints diff --git a/openspec/changes/pipelinq-object-store/specs/pipelinq-object-store/spec.md b/openspec/changes/pipelinq-object-store/specs/pipelinq-object-store/spec.md deleted file mode 100644 index d36618fc..00000000 --- a/openspec/changes/pipelinq-object-store/specs/pipelinq-object-store/spec.md +++ /dev/null @@ -1,331 +0,0 @@ ---- -status: implemented ---- -# pipelinq-object-store Specification - -## Purpose -Define the Pinia-based object store that provides the data layer for Pipelinq. The store uses `createObjectStore` from `@conduction/nextcloud-vue` to query OpenRegister directly from the frontend for all CRUD, search, pagination, file management, audit trails, and relation resolution operations. - -## Context -Pipelinq follows the same thin-client architecture as all Conduction Nextcloud apps: no backend CRUD controllers, all data operations go directly from the Vue frontend to OpenRegister's REST API. The object store is powered by the shared `@conduction/nextcloud-vue` library, which provides `createObjectStore()` -- a factory function that returns a Pinia store with full CRUD capabilities, pagination, caching, loading/error state management, and plugin support. Pipelinq extends the base store with three plugins: `filesPlugin` (file attachments), `auditTrailsPlugin` (audit trail integration), and `relationsPlugin` (cross-entity reference resolution). Object types are registered dynamically at runtime based on register/schema IDs fetched from the app's settings endpoint. - -## Requirements - -### Requirement 1: Object store MUST use createObjectStore from shared library -The store MUST use `createObjectStore('object')` from `@conduction/nextcloud-vue` to create a Pinia store with standardized CRUD operations and plugins. - -#### Scenario 1.1: Store creation with plugins -- GIVEN the file `src/store/modules/object.js` -- WHEN the module is imported -- THEN it MUST export `useObjectStore` created by `createObjectStore('object', { plugins: [...] })` -- AND the plugins array MUST include `filesPlugin()`, `auditTrailsPlugin()`, and `relationsPlugin()` -- AND the Pinia store ID MUST be `'object'` to maintain compatibility with shared library components - -#### Scenario 1.2: Store singleton pattern -- GIVEN multiple Vue components calling `useObjectStore()` -- WHEN each component accesses the store -- THEN they MUST all receive the same Pinia store instance -- AND state changes in one component MUST be reactive in all others - -#### Scenario 1.3: Store available after Pinia initialization -- GIVEN `main.js` registers `PiniaVuePlugin` and creates the Vue instance with `pinia` -- WHEN components call `useObjectStore()` in `setup()` or `computed` -- THEN the store MUST be accessible without errors -- AND it MUST NOT be called before the Vue instance is created (causes "Pinia not installed" error) - -### Requirement 2: Object store MUST support dynamic object type registration -The store MUST support registering object types at runtime, mapping each type name to an OpenRegister register/schema pair. - -#### Scenario 2.1: Register object type from settings -- GIVEN the settings store has fetched config with `register: "5"` and `client_schema: "40"` -- WHEN `objectStore.registerObjectType('client', '40', '5')` is called during `initializeStores()` -- THEN the store's internal `objectTypeRegistry` MUST map `'client'` to `{ schema: '40', register: '5' }` -- AND subsequent CRUD calls for type `'client'` MUST use register 5 and schema 40 - -#### Scenario 2.2: Register all Pipelinq object types -- GIVEN the settings config returns IDs for all configured schemas -- WHEN `initializeStores()` calls `registerObjectType` for each -- THEN the following types MUST be registered (if their schema IDs are present): `client`, `request`, `contact`, `lead`, `prospect`, `pipeline`, `pipelineStage`, `product`, `leadSource`, `requestChannel` -- AND types with empty or missing schema IDs MUST be skipped without error - -#### Scenario 2.3: Guard against unregistered type operations -- GIVEN object type `invoice` has NOT been registered -- WHEN a component calls `objectStore.fetchCollection('invoice', {})` -- THEN the store MUST log a warning to the console -- AND it MUST return an empty array (or null) without throwing an exception -- AND `loading.invoice` MUST remain `false` - -#### Scenario 2.4: Type registry is reactive -- GIVEN object types are registered during `initializeStores()` -- WHEN a component accesses `objectStore.objectTypeRegistry` -- THEN the registry MUST be a reactive Pinia state property -- AND components watching the registry MUST update when types are added - -#### Scenario 2.5: Re-registration overwrites previous mapping -- GIVEN type `client` was registered with schema 40, register 5 -- WHEN `registerObjectType('client', '41', '6')` is called again (e.g., after settings change) -- THEN the registry MUST update to `{ schema: '41', register: '6' }` -- AND the store MUST clear any cached data for the old schema - -### Requirement 3: Object store MUST fetch collections from OpenRegister -The store MUST provide a `fetchCollection` action that queries OpenRegister's list endpoint with pagination, filtering, sorting, and search support. - -#### Scenario 3.1: Fetch paginated collection -- GIVEN object type `client` is registered with register=6, schema=40 -- WHEN `fetchCollection('client', { _limit: 20, _offset: 0 })` is called -- THEN the store MUST make a GET request to `/apps/openregister/api/objects/6/40?_limit=20&_offset=0` -- AND the response results MUST be stored in the store's collections state for type `client` -- AND pagination metadata (total count, current page, limit) MUST be stored for type `client` - -#### Scenario 3.2: Fetch with search query -- GIVEN the user searches for "Gemeente Amsterdam" -- WHEN `fetchCollection('client', { _search: 'Gemeente Amsterdam' })` is called -- THEN the query string MUST include `_search=Gemeente+Amsterdam` -- AND results MUST reflect the search filter applied server-side by OpenRegister - -#### Scenario 3.3: Fetch with field filters -- GIVEN the user filters requests by status "open" -- WHEN `fetchCollection('request', { '_filters[status]': 'open' })` is called -- THEN the query string MUST include `_filters%5Bstatus%5D=open` -- AND only requests with status "open" MUST be returned - -#### Scenario 3.4: Fetch with sorting -- GIVEN the user sorts clients by name ascending -- WHEN `fetchCollection('client', { _order: JSON.stringify({ name: 'asc' }) })` is called -- THEN the query MUST include the `_order` parameter -- AND results MUST be returned in alphabetical order by name - -#### Scenario 3.5: Empty collection response -- GIVEN a search that matches no results -- WHEN `fetchCollection('client', { _search: 'nonexistent12345' })` is called -- THEN the store MUST set `collections.client` to an empty array -- AND `pagination.client.total` MUST be 0 -- AND `loading.client` MUST be set to `false` - -### Requirement 4: Object store MUST fetch individual objects by ID -The store MUST provide a `fetchObject` action that retrieves a single object by its UUID. - -#### Scenario 4.1: Fetch single object -- GIVEN object type `client` is registered with register=6, schema=40 -- WHEN `fetchObject('client', 'uuid-456')` is called -- THEN the store MUST make a GET request to `/apps/openregister/api/objects/6/40/uuid-456` -- AND the object MUST be stored in the store's objects state keyed by `'uuid-456'` - -#### Scenario 4.2: Return cached object if available -- GIVEN `fetchObject('client', 'uuid-456')` was called previously and the object is cached -- WHEN `fetchObject('client', 'uuid-456')` is called again without force flag -- THEN the store MAY return the cached object without making a network request -- AND components MUST receive the cached data immediately - -#### Scenario 4.3: Force refresh bypasses cache -- GIVEN a cached client object with ID `uuid-456` -- WHEN `fetchObject('client', 'uuid-456', { force: true })` is called -- THEN the store MUST make a new GET request to OpenRegister -- AND the cache MUST be updated with the fresh response - -#### Scenario 4.4: Fetch non-existent object -- GIVEN no object exists with ID `uuid-999` -- WHEN `fetchObject('client', 'uuid-999')` is called -- THEN the store MUST handle the 404 response gracefully -- AND `errors.client` MUST contain an error message -- AND the store MUST NOT store null/undefined in the objects state - -#### Scenario 4.5: getObject getter for synchronous access -- GIVEN a client object with ID `uuid-456` is in the store -- WHEN a component accesses `objectStore.getObject('client', 'uuid-456')` -- THEN it MUST return the cached object synchronously (no API call) -- AND if the object is not cached, it MUST return `null` or `undefined` - -### Requirement 5: Object store MUST support create, update, and delete operations -The store MUST provide actions for full CRUD operations against OpenRegister. - -#### Scenario 5.1: Create new object -- GIVEN object type `request` is registered with register=6, schema=42 -- WHEN `saveObject('request', { title: 'New request', client: 'uuid-456' })` is called with no `id` field -- THEN the store MUST POST to `/apps/openregister/api/objects/6/42` -- AND the response (with server-assigned ID) MUST be added to the store's objects state -- AND the collections cache for type `request` MUST be invalidated - -#### Scenario 5.2: Update existing object -- GIVEN a client object exists with ID `uuid-456` -- WHEN `saveObject('client', { id: 'uuid-456', name: 'Updated Name', email: 'new@example.nl' })` is called -- THEN the store MUST PUT to `/apps/openregister/api/objects/6/40/uuid-456` -- AND the store MUST update `objects.client['uuid-456']` with the response data - -#### Scenario 5.3: Delete object -- GIVEN a request object exists with ID `uuid-789` -- WHEN `deleteObject('request', 'uuid-789')` is called -- THEN the store MUST DELETE `/apps/openregister/api/objects/6/42/uuid-789` -- AND the object MUST be removed from `objects.request` -- AND the collections cache for type `request` MUST be invalidated - -#### Scenario 5.4: Optimistic update on save -- GIVEN a client object is being updated -- WHEN `saveObject()` is called -- THEN the store MAY apply the update optimistically (update local state before API response) -- AND if the API call fails, the store MUST revert to the previous state -- AND the error MUST be recorded in `errors.client` - -#### Scenario 5.5: Validation error on create -- GIVEN the OpenRegister schema requires field `name` on clients -- WHEN `saveObject('client', { email: 'test@example.nl' })` is called without `name` -- THEN OpenRegister MUST return a 422 validation error -- AND the store MUST capture the validation error details in `errors.client` -- AND `loading.client` MUST be set to `false` - -### Requirement 6: Object store MUST track loading and error states per type -The store MUST provide reactive loading and error states for each registered object type. - -#### Scenario 6.1: Loading state during collection fetch -- GIVEN a collection fetch is in progress for type `client` -- WHEN a component checks `objectStore.loading.client` (or equivalent getter) -- THEN it MUST return `true` -- AND when the fetch completes (success or error), it MUST return `false` - -#### Scenario 6.2: Loading state during single object fetch -- GIVEN a single object fetch is in progress for type `request` -- WHEN a component checks the loading state -- THEN it MUST return `true` for the specific operation -- AND components MUST be able to show `NcLoadingIcon` based on this state - -#### Scenario 6.3: Error state on network failure -- GIVEN the OpenRegister API is unreachable -- WHEN a fetch call fails with a network error -- THEN the error MUST be stored in the store's error state for the relevant type -- AND `console.error` MUST log the error details -- AND the loading state MUST be set to `false` - -#### Scenario 6.4: Error state cleared on successful retry -- GIVEN a previous fetch for type `client` failed with an error -- WHEN a subsequent fetch for the same type succeeds -- THEN the error state for `client` MUST be cleared (set to null/empty) - -#### Scenario 6.5: Concurrent loading states for different types -- GIVEN fetches are in progress for both `client` and `request` simultaneously -- WHEN a component checks loading states -- THEN `loading.client` and `loading.request` MUST independently reflect their respective states -- AND completion of one MUST NOT affect the other - -### Requirement 7: Settings store MUST load configuration before data operations -The settings store MUST fetch app settings on initialization, providing register/schema IDs needed for object type registration. - -#### Scenario 7.1: Settings fetch on app load -- GIVEN the app is loading for the first time -- WHEN `initializeStores()` is called in `main.js` -- THEN the settings store MUST fetch `GET /apps/pipelinq/api/settings` with CSRF token and OCS header -- AND the response MUST populate `config`, `openRegisters`, and `isAdmin` in the settings store - -#### Scenario 7.2: Object types registered from settings config -- GIVEN the settings fetch returns `{ config: { register: '6', client_schema: '40', request_schema: '42', contact_schema: '43' }, openRegisters: true }` -- WHEN `initializeStores()` processes the config -- THEN it MUST call `objectStore.registerObjectType('client', '40', '6')` -- AND it MUST call `objectStore.registerObjectType('request', '42', '6')` -- AND it MUST call `objectStore.registerObjectType('contact', '43', '6')` -- AND types with empty string values MUST be skipped - -#### Scenario 7.3: Settings fetch failure -- GIVEN the settings endpoint returns a 500 error -- WHEN the settings store processes the failure -- THEN `settingsStore.error` MUST contain the error message -- AND `settingsStore.initialized` MUST remain `false` -- AND the App.vue MUST display a loading state (since `storesReady` depends on initialization) - -#### Scenario 7.4: Settings save action -- GIVEN an admin user changes the register configuration -- WHEN `settingsStore.saveSettings({ register: '7', client_schema: '50' })` is called -- THEN it MUST POST to `/apps/pipelinq/api/settings` with the JSON body -- AND on success, `settingsStore.config` MUST be updated with the response - -#### Scenario 7.5: Settings provide isAdmin and hasOpenRegisters -- GIVEN the settings fetch returns `{ openRegisters: true, isAdmin: true }` -- WHEN components check `settingsStore.hasOpenRegisters` and `settingsStore.getIsAdmin` -- THEN the getters MUST return the correct boolean values -- AND App.vue MUST use `hasOpenRegisters` to decide whether to render the main content or the missing-dependency screen - -### Requirement 8: All API calls MUST include Nextcloud authentication headers -Every HTTP request to OpenRegister or the app's own API MUST include CSRF token and OCS authentication headers. - -#### Scenario 8.1: CSRF token on every request -- GIVEN a store action makes a fetch call to any Nextcloud API -- WHEN the request headers are constructed -- THEN it MUST include `requesttoken: OC.requestToken` -- AND `OC.requestToken` MUST be read from the global `OC` object at request time (not cached at module load) - -#### Scenario 8.2: OCS header on every request -- GIVEN a store action makes a fetch call -- WHEN the request headers are constructed -- THEN it MUST include `OCS-APIREQUEST: true` -- AND it MUST include `Content-Type: application/json` for POST/PUT requests - -#### Scenario 8.3: Authentication failure handling -- GIVEN the CSRF token has expired (session timeout) -- WHEN a fetch call returns a 401 or CSRF validation error -- THEN the store MUST handle the error gracefully -- AND it MAY trigger a page reload to refresh the token - -### Requirement 9: Files plugin MUST support file operations on objects -The `filesPlugin()` MUST add file upload, download, and listing capabilities to the object store. - -#### Scenario 9.1: Upload file to object -- GIVEN a client object with ID `uuid-456` -- WHEN the user uploads a document via the file attachment UI -- THEN the files plugin MUST POST the file to OpenRegister's file endpoint for that object -- AND the file metadata MUST be stored as part of the object's file references - -#### Scenario 9.2: List files for object -- GIVEN a request object with 3 attached files -- WHEN the detail view loads and requests file listing -- THEN the files plugin MUST fetch the file list from OpenRegister -- AND each file entry MUST include filename, size, mime type, and download URL - -#### Scenario 9.3: Download file from object -- GIVEN a file attached to a client object -- WHEN the user clicks the download button -- THEN the files plugin MUST initiate a download from OpenRegister's file endpoint -- AND the file MUST be served with the correct Content-Disposition header - -### Requirement 10: Relations plugin MUST resolve cross-entity references -The `relationsPlugin()` MUST automatically resolve references between related objects (e.g., request -> client). - -#### Scenario 10.1: Resolve client reference on request -- GIVEN a request object with field `client: 'uuid-456'` -- WHEN the request detail view loads -- THEN the relations plugin MUST detect the client reference and fetch the full client object -- AND the resolved client data MUST be available alongside the request data - -#### Scenario 10.2: Resolve multiple references -- GIVEN a client object with multiple request references -- WHEN the client detail view loads -- THEN the relations plugin MUST resolve all referenced requests -- AND the resolved requests MUST be available as a collection - -#### Scenario 10.3: Circular reference protection -- GIVEN object A references object B which references object A -- WHEN the relations plugin resolves references -- THEN it MUST detect the cycle and stop resolution after one level -- AND it MUST NOT enter an infinite loop - ---- - -## Current Implementation Status - -**Implemented via shared library.** The Pipelinq object store exists and uses `@conduction/nextcloud-vue` rather than a custom implementation. - -**Implemented (with file paths -- in `pipelinq/` submodule):** -- **Object store**: `pipelinq/src/store/modules/object.js` -- uses `createObjectStore('object')` from `@conduction/nextcloud-vue` with `filesPlugin()`, `auditTrailsPlugin()`, and `relationsPlugin()`. The shared library provides all CRUD, pagination, caching, search, loading/error state tracking, and authentication headers. -- **Settings store**: `pipelinq/src/store/modules/settings.js` -- fetches `/apps/pipelinq/api/settings` to get register/schema configuration, with loading and error state tracking. Includes `fetchSettings()` and `saveSettings()` actions with CSRF token and OCS headers. -- **Store initialization**: `pipelinq/src/store/store.js` -- `initializeStores()` fetches settings then registers all object types from the config. -- **Authentication headers**: Both stores include `requesttoken: OC.requestToken` and `OCS-APIREQUEST: true` in all fetch calls. - -**Architecture note:** The spec describes both the generic shared library API and Pipelinq-specific type registration. The `createObjectStore()` function provides `registerObjectType()`, `fetchCollection()`, `fetchObject()`, `saveObject()`, `deleteObject()`, and loading/error state tracking internally. Pipelinq-specific code only needs to call `registerObjectType()` with the correct schema/register IDs during initialization. - -## Standards & References - -- **Nextcloud authentication**: CSRF token via `OC.requestToken` and OCS header per Nextcloud API conventions. -- **OpenRegister API**: REST API at `/apps/openregister/api/objects/{register}/{schema}` for all CRUD operations. -- **Pinia**: State management with Vue 2 compatibility via `PiniaVuePlugin`. -- **@conduction/nextcloud-vue**: Shared library providing `createObjectStore`, used by Procest, Pipelinq, Softwarecatalog, and other Conduction apps. - -## Specificity Assessment - -This spec is highly detailed with 10 requirements and comprehensive scenarios. It documents both the shared library pattern (how `createObjectStore` works) and the Pipelinq-specific configuration (which types are registered, what settings are needed). The spec is implementable as-is, and the core functionality is already implemented via the shared library. diff --git a/openspec/changes/procest-manifest-v1/builds/build.json b/openspec/changes/procest-manifest-v1/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/procest-manifest-v1/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/procest-store-migration/builds/build.json b/openspec/changes/procest-store-migration/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/procest-store-migration/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/register-i18n/builds/build.json b/openspec/changes/register-i18n/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/register-i18n/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/stuf-support/builds/build.json b/openspec/changes/stuf-support/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/stuf-support/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/werkvoorraad/builds/build.json b/openspec/changes/werkvoorraad/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/werkvoorraad/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/workflow-definition-model/builds/build.json b/openspec/changes/workflow-definition-model/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/workflow-definition-model/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/zaak-intake-flow/builds/build.json b/openspec/changes/zaak-intake-flow/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/zaak-intake-flow/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +} diff --git a/openspec/changes/zgw-api-mapping/builds/build.json b/openspec/changes/zgw-api-mapping/builds/build.json new file mode 100644 index 00000000..7ce60031 --- /dev/null +++ b/openspec/changes/zgw-api-mapping/builds/build.json @@ -0,0 +1,5 @@ +{ + "status": "applied", + "applied_at": "2026-05-11", + "marker": "deferred-recovery" +}