Skip to content

Release: merge development into beta - #3980

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

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

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

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

Merging this PR will trigger the beta release workflow.

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

… relation's cases on the map

The contact's Woonadres card and the organisation's KvK-and-address card
declared their link as a bare page name, which the facts widget does not
draw (it reads a route object), so the link the board shows never
rendered. It is now a route object to the cases list in map view,
narrowed to this relation as requester.

Assisted-by: ClaudeCode:claude-opus-5-5
The example data the setup wizard loads was three generated placeholder
objects per schema, so a fresh install still showed empty work pages.
Curated fragments in lib/Settings/demo.d now load with the generated set:
five Woo requests (four assigned to whoever loads the set, one overdue,
two received today), four documents on one of them assessed as public,
partly public and not public with citable refusal grounds, and an AVG
request case type with three data subject requests. Everything is
fictional.

ExampleDataFragments fills the load-time tokens: @me for the loading
user (left out when nobody is signed in), @today±Nd and @now±Nd, so the
deadlines fall around the day the set is loaded. A test holds every
fragment object to its schema, because OpenRegister drops undeclared
properties without a word.

Assisted-by: ClaudeCode:claude-opus-5-5
A status name is a translatable field, so a statusType row read through
ObjectService::searchObjects() carries it as a language map
({"nl": "Publicatie"}). DwellTimeAnalyzer and TransitionMatrixBuilder
accepted only a string and fell back to the status id, so every phase of
a case type whose statuses were seeded with a language map (the Woo
request's 3c0f5a00-…-b00x) showed as a raw id on DqProcesanalyse and was
left out of the days-per-phase matrix. Both now read the name through
LanguageMapText, the helper the rest of dossiq uses for translatable
titles.

Assisted-by: ClaudeCode:claude-opus-5-5
A new load-time token, @mygroup, is the loading user's first group other
than admin, by id. The Afvalinzameling Woo request and the correctie AVG
request carry it as assignedGroup with nobody assigned, so they land in
the team queue on My team (DqMijnTeam). For a user in no such group, or
with nobody signed in, the property is left out.

Assisted-by: ClaudeCode:claude-opus-5-5
…DqZaakBetrokkenen)

The parties grouped by role come first, titled Partijen, as the board
draws them; the linked contacts follow as Contacten, then the roles' seats
and the followers. Every card stays.

Assisted-by: ClaudeCode:claude-opus-5-5
…WIP, audit part 1)

A case type title and a status name arrive from OpenRegister as a language
map. These readers cast it, so they produced "Array":
- LeaverHandoverService::draftsOf(): the draft case type's title
- KpiAggregationService::caseTypeTitles(): the team queue's case type names
- RequestCatalogue::entries(): the intake catalogue's title and description
- DemoCaseloadSeedDataService::statusTypesByCaseTypeAndName(): the status
  lookup key, so a seeded status was never found by its name
Each now reads through LanguageMapText, with a test on a language-map row.

Assisted-by: ClaudeCode:claude-opus-5-5
Where a case-object link's objectUrl is OpenRegister's objects endpoint, the
object-types endpoint resolves that schema and returns its title, property
count and views (map when a property is a geo type, table always); free-text
types stay empty. The catalogue table gains Schema and Display columns
(board DqCatalogus).

Assisted-by: ClaudeCode:claude-opus-5-5
…vice

The case page draws its What now? card per status role. An objection's
"Advies uitgebracht" and a tender's "Beoordeling" now carry the review
role (bezwaar-beroep 1.4.0, aanbesteding 1.3.0, and the seeded Bezwaar
statuses), so the card for deciding shows there; no case type gives
one role to two statuses. The LHS advice page declares its card per
final intervention: a pre-announcement and a two-week period for a view
for a penalty order or enforcement, the letter for a warning.

Assisted-by: ClaudeCode:claude-opus-5-5
…P, audit part 2)

Role type names and descriptions, content template names and bodies,
status public labels and descriptions, obligation titles and decision and
document type names arrive from OpenRegister as a language map. These
readers cast or compared them as strings:
- MunicipalRoleSetService: seeding by name (re-seeded the set every
  time), the offer and the role-in-use name
- ContentTemplateService: offered names, bodies and search, apply's body
- DomainCopyService: the name of a template that was not carried
- ShippedConfigurationService: the overview title
- StatusTypeLookup::announcedLabelOf(), CaseStatusStore::lookupStatusDescription()
- TransitionPreconditions: the blocking obligation's title
- ZgwBrcRulesService: a decision type matched by its description
- BrcDocumentLinkRules: the document type name an allowed-list is checked against
- ZtcRelatedTypeLookup: the name the same-named document types are searched by
Each reads through LanguageMapText (ZgwRulesBase gains textOf() for its
subclasses), with a test on a language-map row. The in-memory test
register can now refuse a save, for the not-carried path.

Assisted-by: ClaudeCode:claude-opus-5-5
A detail page hands a chart its content keys as props, and the chart's
kind prop is `type`; `chartKind` is only the dashboard dispatcher's
name for it. The advice page declared `chartKind: matrix` alone, so
the chart fell back to an empty area chart. The widget now also sets
`type: matrix`, and the spec holds it.

Assisted-by: ClaudeCode:claude-opus-5-5
…y requests name themselves in the Actief row (DqContacten, DqOrganisaties, DqPrivacy)

A selected tab now shows as an active-filter chip (activeLabel), as the
boards draw "Actief:" over these lists.

Assisted-by: ClaudeCode:claude-opus-5-5
… count

Assisted-by: ClaudeCode:claude-opus-5-5
…reader's language

/api/case-types/{id}/phases passed a status's translatable name through as
the raw language map, and the case type page's phase board feeds it to a
widget that renders "[object Object]". The name is now read through
TranslatedText, in the reader's language with the usual fallbacks. No
other consumer of the endpoint reads the map, so no raw field is kept.

Assisted-by: ClaudeCode:claude-opus-5-5
The detail host passes a chart only the content keys it declares, and
the chart's kind prop is `type`. A spec now walks every detail page of
both profiles and fails on a chart without `type`. The LHS matrix on
the advice page was the only one; the other charts sit on dashboards,
whose dispatcher reads `chartKind`.

Assisted-by: ClaudeCode:claude-opus-5-5
…tion

Activate has no backing action yet; only publish, deprecate and chain exist.

Assisted-by: ClaudeCode:claude-opus-5-5
…-batch-9

# Conflicts:
#	src/menu-layout.simple.json
#	tests/vitest/simpleCasePage.spec.js
…e case

Board DqArchiveren draws archiving as its own dialog: the case, what the
case type keeps, "Klaar om te archiveren", a reason and the read-only
note. CaseArchiveDialog posts the same archive act the Lifecycle dialog
posts, behind the same role check and the same refusal of an open case,
and is offered under More on an ended case (case-archive) and by the
closed stage card. The Lifecycle dialog keeps its Archive act.

The checklist is GET /api/case/{caseId}/archive/readiness
(CaseArchiveReadiness behind CaseActsOverview): ended, result recorded,
decisions, documents final out of all, and open tasks, each read off the
stored case; a task list the engine could not read is unknown, not zero.
Only "ended" blocks, as the archive act itself only refuses that. A case
type can add its own items through the action's `checks` prop.

Assisted-by: ClaudeCode:claude-opus-5-5
…dq-batch-9

# Conflicts:
#	src/menu-layout.simple.json
#	tests/vitest/simpleCasePage.spec.js
…dq-batch-9

# Conflicts:
#	src/menu-layout.simple.json
rubenvdlinde and others added 30 commits October 20, 2026 13:08
Library batch 20. Syncs the vendored manifest schema to the installed one for
the lag check.

Assisted-by: ClaudeCode:claude-opus-5-5
…ion/dq-batch-18

MenuCaseTypesService rows carry both development's identifier and the
branch's icon. In src/manifest.json the branch's colour, icon and teal-tone
edits to Cases, CaseTypes and CaseTypeDetail are applied onto development's
pages; the Queue page the branch still carried stays retired. The result
equals a structural 3-way merge of the manifest.

Assisted-by: ClaudeCode:claude-opus-5-5
…gration/dq-batch-18

# Conflicts:
#	tests/schemas/app-manifest-v2.schema.json
…ntegration/dq-batch-18

# Conflicts:
#	src/dialogs/CaseLocationDialog.vue
…q-batch-18

# Conflicts:
#	src/menu-layout.simple.json
…ation/dq-batch-18

# Conflicts:
#	src/manifest.json
lib/Service/StatusTypePalette.php: recolour() has a cyclomatic complexity of
11 against phpmd's 10, on the branch's own head and with a fresh HOME.
Dropped from batch 18; back to BLD-Dq-PILLS.

This reverts merge 4c428d0.

Assisted-by: ClaudeCode:claude-opus-5-5
- caseTypeColourIcon: the Queue, the one list with case type tags, is a view
  on Cases now, so the spec no longer requires a tag to exist; any tag must
  resolve. Teal is checked where the test's name says, on the Cases board
  columns and map markers.
- simpleListAndDashboard: the map list's second line is the case address with
  the identifier as fallback (fix/zakenkaart-map-list); a view link with a
  saved-view route is not matched to a menu entry by route.

Assisted-by: ClaudeCode:claude-opus-5-5
…r schema allows

feat/case-type-colour-icon adds teal and teal-light to statusType.colour, and
the statuses editor from development drew a swatch only for the colours it
knew. Its two swatches use the same palette tokens as the case type colour.

Assisted-by: ClaudeCode:claude-opus-5-5
feat(screens): round 18 dossiq batch 18
* feat(case): the case page lists its links in one Gekoppeld side card

DqZaak draws projects inside the Gekoppeld side card with the other apps'
links, not as a widget of their own. The simple case page now places
nextcloud-vue's linked widget after Handling with the documents, fees,
contract and projects leaves, and the standalone case-projects widget and
its grid cell are gone.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(case): full profile keeps the Linked card; money panels mount shillinq's render leaves

The full profile has no side column, so removing the Projects widget lost
projects there. It now places the same linked card in that grid cell; the
simple profile drops the cell. The fees and contract placements named
shillinq's data leaves, which no host can mount, so they drew nothing; they
now name the -panel render leaves.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(settings): personal settings open with Views in my menu

Board DqPersoonlijkeInstellingen (design-system #305) opens the personal
settings with "Weergaven in mijn menu": the pinned My views entries,
reordered, taken out with "Uit menu halen", and a "Weergave toevoegen"
picker. The dialog now draws the library's `views` section first, where
the case type picker was. Needs nextcloud-vue feat/saved-view-menu-settings;
a library without the key ignores it.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(cases): the case page's stage dates, what-now title and people tab as the boards draw them

- The header stages read the date each status was reached from the case
  type blueprint (datesSource), so a stage reads "Ontvangst / 14 okt" as
  the boards do.
- In progress, the what-now card reads "Wat nu? Stap 2: {status label}"
  and "Daarna: naar stap 3, besluit" (DqZaak), with the old title as the
  fallback. Needs nextcloud-vue with next-step title tokens (schema
  2.134.0).
- The People tab puts seats and followers side by side, half a row each
  (DqZaakBetrokkenen).

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(cases): the assistant as a tab on the case page

DqZaakAssistent draws the assistant inside the case page, in the
AI-toezicht tab; it was reachable only through the launcher's floating
window. The simple case page gets an AI oversight tab holding the same
AI Chat Companion, embedded through nextcloud-vue's `ai-chat` widget,
with the case as its context and hermiq required. It waits under Acties
with the other panels and joins the strip while open. The hex and the
Assistant sidebar tab stay.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(cases): the case's Tasks tab is the library task table

The simple case page's Tasks tab draws the case's tasks with the library
task table (type tasks, variant table) instead of dossiq's case task
pane: every task on the case, open first and finished struck through,
the summary under the title and New task in the card header (board
DqZaakTaken). Claim, complete (also a colleague's task; the engine
decides), cancel and adding a task stay on the case page; a task's form,
candidates and notes are on its own page, which the title opens. The
view-all link still opens Tasks filtered on the case.

Needs nextcloud-vue with the record task table (r20/board-retention-tasks).

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(cases): say on the case that advice is still asked

DqZaak opens the side column with "Advies gevraagd aan Mobiliteit en
verkeer. Antwoord uiterlijk 10 oktober. Afronden kan pas daarna."
while advice is open on the case. The simple profile's case page now
opens its side column with a banner that shows while an open advice
request (adviesAanvraag, status requested) points at this case and
names the soonest due one. Configuration only; it needs nextcloud-vue
notice-scoped-to-the-record for the @objectid token in a banner source.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(case-types): the side column follows the open tab

The case type page showed Versions and Data beside every tab and drew the
publish check in the Overview body. The boards change the side column
per tab: Overview has Versions and Check (DqZaaktypeOverzicht), Phases
and tasks has Versions, Data and History (DqZaaktype), Fields has the
parent type (DqZaaktypeVelden). Every tab now has an id and each side
card names the tabs it stands beside, which the library already reads.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(doorlooptijd): the monthly chart fits its tile with its source line under the plot

The plot was pinned at 260px in a gridHeight 4 tile, which cut it off and
scrolled (DqDoorlooptijd). Without a height a dashboard chart fits its
tile. The source line moves from the card's subtitle to the chart's
footnote, under the plot, where the board draws it.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(case-types): the Overview carries the board's cards and the phase board opens the flow

Board DqZaaktypeOverzicht draws Statuses, Rules, Rights, Personal data and
Translations on the Overview; the page showed Statuses and Rights only.
Overview now lists all five in the board's order, with a Rules card on
the case schema's rule inventory. The phase board links "View as flow"
to the case type's workflow (board DqZaaktype), which needs nextcloud-vue
feat/header-link-param-tokens to resolve the id.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(organisations): suspend, resume and end an organisation from its page

Board DqTenant draws Opschorten in an organisation's header and says
Beëindigen is under More; the page offered neither. Suspend (while
active) and Resume (while suspended) are header actions and End the
organisation is under More, each calling OpenRegister's organisation
lifecycle endpoint. Edit and the History tab have no backing function
yet and are listed in the change.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(vth): inspection checklists are library rows with an accessible pill

The VTH tab drew each checklist as a box with Edit and Delete buttons and
an Actief badge whose text took the main ink on the solid success colour
under the Zuiddrecht theme: 2.59:1, failing WCAG AA. Board
DqVthInstellingen draws list rows with a pill. The checklists are now
CnObjectRow rows with a CnStatusBadge (5.77:1) and Edit and Delete in the
row menu.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(doorlooptijd): the case type choice is a page filter on the description line

DqDoorlooptijd draws 'alle zaaktypen' on the description line, before the
period group; live mounted a bespoke select in the header's action slot,
where the board draws Downloaden. The choice is now a declarative page
filter (nextcloud-vue: select filters on the description line, library
batch 21) that keeps what the bespoke one did: only case types with a
processing deadline, clearable to every case type, writing caseType into
the workspace the widgets read. DtCaseTypeFilter is removed.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(features): the overlay carries each feature's category

The pre-commit hook regenerates docs/features.json from the overlay with
ConductionNL/.github's extract-features.py on every spec commit.
`category`, which the dashboard groups the Features tab by, lived only in
the generated file, so each spec commit dropped it and
DashboardControllerContractTest broke twice. Every overlay entry now
carries its category. The generator is fixed in ConductionNL/.github
(fix/extract-features-keeps-fields): it reads `category` from the overlay
and keeps any field the existing file holds that its sources do not give.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(case-page): a case type names the special categories of personal data it processes

DqSociaalDomein draws 'Bijzondere persoonsgegevens: gezondheid.' at the
top of a Melding Jeugdwet case's side column. A case type now lists the
GDPR article 9 and 10 categories its cases process
(caseType.specialDataCategories, caseType schema 1.27.0), and the case
page has one warning card per category, shown when the case type lists
it and placed by the case type's sideCards. Melding Jeugdwet lists
health and puts its card first. The board's second sentence ('Elke
inzage wordt gelogd met het doel') is left out: dossiq does not record a
purpose per view, so the card would claim what does not happen.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(case-types): Dutch for the parent type side card

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(statuses): a shipped status takes the colour its boards draw, again

Batch 18 merged fcb959f and reverted it (3dbd7be): phpmd measured
StatusTypePalette::recolour() at complexity 11 against 10. This brings the
change back on top of development and splits recolour() into
boardColourOf(), isUnchosen() and wasShipped(); the rule is the same. The
teal entries in the board colour maps are development's own now (batch
18 added teal to the statuses editor), so this branch no longer carries
them.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(example-set): jeugdwet case facts, a saved search and Sanne as contact

DqSociaalDomein draws the jeugdwet example case's legal basis, who
reported it, its regisseur and a deadline about five weeks out; the
package example now carries them (assignee fatma.kaya, the cast's new
jeugdteam regisseur, also the case's role participant).

DqZoekresultaten draws a saved search for Lindelaan with its count
alert at 12; the Zuiddrecht set declares it as a public view on the
search page, owned by the cast's Woo coordinator (OpenRegister
example-set views).

DqContact draws the portal resident Sanne de Vries; the set seeds her
as a brpPerson, every field from the cast.

Assisted-by: ClaudeCode:claude-opus-5-5

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

Syncs the vendored manifest schema to the installed one for the lag check.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(doorlooptijd): one case type page filter on the processing time page

development moved the case type filter onto a pageFilters entry and
r20/doorlooptijd-case-type-in-description did the same with the board's
description placement; the merge kept both keys, so the JSON carried a
duplicate pageFilters. Keeps the branch's entry, which adds the placement and
label field and uses openregister's _isnull filter grammar.

Assisted-by: ClaudeCode:claude-opus-5-5

* test: the side column keeps the Linked card and the lag check reads actionFields

feat/case-linked-card puts side-linked after Handling on every case and
feat/advice-requested-notice extended the generic side cards without it, so
the merged column lists both. nextcloud-vue schema 2.136.0 (2.114.0) closes
an action through $defs/actionFields and unevaluatedProperties instead of
additionalProperties, so the _note check reads the new shape.

Assisted-by: ClaudeCode:claude-opus-5-5

* test(schemas): record the digest of the caseType special-data fragment

r20/case-special-data-notice ships 99-case-type-special-data.json at caseType
1.27.0 without a recorded digest, which SchemaVersionFloorTest refuses.

Assisted-by: ClaudeCode:claude-opus-5-5
…4115)

* feat(ai-oversight): report every AI suggestion on a case to hermiq

The case's AI features panel asks what the handler does with each answer and
feature run (use, adapt, reject) and reports it; a suggestion nobody acted on
is reported ignored as superseded, closed or expired. Redaction proposals
report their review and their supersession. record-action now checks case
read access before writing, accepts ignored with unusedBecause, and the
delegation names dossiq as the origin app. Without hermiq it stays a no-op.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(ai-oversight): format, @SPEC tags, catalogue and version bumps for the dispatch

Prettier on the new files, @SPEC on every new method, en and nl entries for
aiAuditEntry.unusedBecause, the rebuilt l10n js catalogues, and the register
(0.20.60) and app version bumps that make the aiAuditEntry change import on an
existing install.

Assisted-by: ClaudeCode:claude-opus-5-5
* feat(example-set): the Lindelaan Woo case is portaliq's

The Zuiddrecht set wrote two "Verlichting fietspad Lindelaan" cases:
portaliq's, Sanne de Vries's request through Mijn Zuiddrecht, and
dossiq's, with Linda Bakker as requester. The DqZaak boards draw
Sanne's. Dossiq's fragment drops its case and everything on it
(contact moments, documents and their files, tasks, advice, term);
portaliq's fragment carries them on Sanne's case. The Woo document
type stays here, described "Document bij een Woo-verzoek", which only
it carries, so portaliq's documents find it by lookup.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(case): hand the loaded case to the attention strip

CaseBannerStack bound only objectId on CaseAttentionPanel. The strip reads
attentionMarkers and riskAssessment off objectData, so on the live case
page neither the markers nor the risk assessment were ever drawn.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(case): write the picked role to the Add party form

RoleTypePicker emitted `select`, but CnFormDialog mounts a form-field
widget listening for `input` and `update:modelValue` only, so a picked
role never reached the form. Emit update:modelValue and read the
modelValue the dialog binds.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(case): read the case title from objectData in the programme section

CaseProgrammeSection declared a prop `object`, but CnDetailWidgetHost
binds the loaded case as `objectData`, so the link dialog always got an
empty case title.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(case): stop the review panel shadowing the library document-review widget

dossiq registered DocumentReviewPanel under `document-review`, the key of
nextcloud-vue's library widget (CnDocumentReviewWidget). The app registry
resolves first, so the app key replaced the library widget for any page
of this app asking for it. Register the panel as
`document-relevance-review` and point the case Review tab at it. The
widget id `case-document-review` is unchanged, so saved arrangements and
the menu layout keep finding it.

Assisted-by: ClaudeCode:claude-opus-5-5

* chore(register): move the register to 0.20.61 and bump the app version

Batch 19 (#4118) and the hermiq AI audit (#4115) each moved the register
from 0.20.59 to 0.20.60 with different content (status type colours; the
aiAuditEntry ignored outcome and unusedBecause). An install that imported one
0.20.60 would not import the other, so the register moves once more.

Assisted-by: ClaudeCode:claude-opus-5-5
* feat(case-page): a case type names the special categories of personal data it processes

DqSociaalDomein draws 'Bijzondere persoonsgegevens: gezondheid.' at the
top of a Melding Jeugdwet case's side column. A case type now lists the
GDPR article 9 and 10 categories its cases process
(caseType.specialDataCategories, caseType schema 1.27.0), and the case
page has one warning card per category, shown when the case type lists
it and placed by the case type's sideCards. Melding Jeugdwet lists
health and puts its card first. The board's second sentence ('Elke
inzage wordt gelogd met het doel') is left out: dossiq does not record a
purpose per view, so the card would claim what does not happen.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(case-page): opening a sensitive case records the reader's purpose

Ruben decided to build purpose logging. A case whose case type lists
special categories of personal data opens only after the reader states
why: POST /api/cases/{id}/access-purpose reads the case as the reader and
writes the purpose to the case's OpenRegister audit trail with the
reader, the time, the purpose, the reader's own words for other and the
categories. An unreadable case, an unknown purpose, an empty other and a
failed write are refused, so the case does not open unrecorded. The case
page declares nextcloud-vue's accessPurpose with the same purposes (needs
detail-access-purpose; schema 2.136.0).

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(case-page): an unreadable case or case type refuses the purpose, never answers null

The architecture rule keeps swallowing catches from growing: a read miss
is a refusal here. An unreadable case answers 404, and an unreadable case
type is not taken to ask nothing, it refuses the open (503).

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(case-page): the special-data notice says every view is logged with its purpose

DqSociaalDomein's notice reads 'Bijzondere persoonsgegevens: gezondheid.
Elke inzage wordt gelogd met het doel.' The sentence was left out while
dossiq recorded no purpose; opening such a case now records the reader's
purpose in its audit trail (case-access-purpose), so each category card
carries it. Built on r20/case-access-purpose with r20/case-special-data-
notice merged in.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(end-of-day): Close out your day is manifest config, with the day worked out on the server

Ruben decided that the custom EndOfDayView moves onto the manifest setup,
losing nothing, with the day lists on the server so Recorded today
survives a reload. GET /api/personal-queue/end-of-day (EndOfDayController,
EndOfDayService) answers what the reader opened today without a note (by
OpenRegister's per-reader read state, never the object's own update), the
reader's notes of today with their time, what comes first tomorrow, the
figures and the sources that could not be read. Each row names its route
and the notes endpoint of its own case or task.

The page is a dashboard over nextcloud-vue widgets: the open list as a
record list of notes (field per row, humaniq's hours leaf per row, one
button for all, the browser-only ready figure, two empty texts), Recorded
today, Your day, First thing tomorrow, How this works, and the crumb to My
work with today's date. EndOfDayView and its client helpers are removed;
the custom-page ratchet goes down by one. Needs nextcloud-vue
record-list-notes.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(end-of-day): register the Check icon the Recorded today list draws

Hydra gate 60: an icon a manifest names and the app does not register
renders as nothing.

Assisted-by: ClaudeCode:claude-opus-5-5

* refactor(lists): deadline, export and personal queue as library config

- Deadline columns use the library `date` cell with `due` and an
  `upcomingLabel`; DeadlineCountdownCell and the dead
  `deadlineCountdown()` helper are gone (daysUntilDeadline and isOverdue
  stay for their other callers).
- The Cases page uses CnIndexPage's own Export menu (`allowExport`, case
  schema `configuration.exportable`, case 1.46.0); CaseListExportAction
  and caseExportHelpers.js are gone.
- /my-queue is a dashboard page with one `work-queue` widget, a
  breadcrumb and an `open-modal` CnActionDialog header action;
  PersonalQueueView and planItemDialog.js are gone.
- Specs ported to manifest assertions; e2e reads the library test id.

Assisted-by: ClaudeCode:claude-opus-5-5

* refactor(cases): retire dead pages, components and registry keys

Removes code nothing mounts: LocationPicker (and the leaflet and
leaflet-draw dependencies it alone used), PropertyDefinitionFields with
its vocabulary and concept-scheme services, InspectionPanel, and the
DeelzaakList and DeelzaakDetail pages. Drops the unused registry keys
BesluitvormingLeafTab, CaseNotesTab and CaseTimelineTab; their
components stay under the keys that mount them. registerNavCardGrid
goes because nextcloud-vue 2.114.0 resolves nav-card-grid through its
built-in widget fallback.

The old sub-case addresses redirect: /cases/:id/deelzaken opens the
parent on its Related tab, /cases/:parentId/deelzaken/:id opens the
sub-case. E2e tests that opened the removed pages or targeted the
#pd-add-* ids are parked with test.fixme and a reason.

Assisted-by: ClaudeCode:claude-opus-5-5

* test(case types): drive the field vocabulary e2e through the manifest field form

The two specs targeted #pd-add-* ids from a component no page mounted.
They now open the case-type-fields-editor form. The form gains the
list entry type (items) for an array field, so a set-valued answer can
be declared there.

Assisted-by: ClaudeCode:claude-opus-5-5

* test(queue): the queue widget names no source

Assisted-by: ClaudeCode:claude-opus-5-5

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

Syncs the vendored manifest schema to the installed one for the lag check.

Assisted-by: ClaudeCode:claude-opus-5-5

* chore(register): move the register to 0.20.62 and bump the app version

The case schema moves to 1.46.0 (the L1 list config) in this batch, so the
register moves once over development's 0.20.61.

Assisted-by: ClaudeCode:claude-opus-5-5

* test(manifest): count the personal queue as a retired custom page

feature/dq-abstract-l1-config-dossiq-only moves PersonalQueue onto library
config without moving the custom page ratchet, so the count read one short.

Assisted-by: ClaudeCode:claude-opus-5-5

* test(architecture): the personal queue widget is the reader's own

feature/dq-abstract-l1-config-dossiq-only puts the personal queue on a
dashboard widget without roles; it shows only what waits on the reader, so it
is listed as public with that reason.

Assisted-by: ClaudeCode:claude-opus-5-5
* feat(tasks): the task leaves are detail widgets

Moving the task page (DqTaak) onto a manifest detail page needs its
task-anchored parts as widgets a manifest can name. The notes, appointments
and history leaves and the waiting-case section are registered as carded
widgets (task-notes, task-events, task-audit, task-waiting-case) and read
the task the page hands them as objectId; the history opens with
content.open. The case card needs no entry: it is the library facts widget.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(tasks): the task page is a manifest detail page

TaskDetailView, a custom view of about 1,000 lines, is replaced by the
TaskDetail detail page on entitySource "tasks", on the same route and page
id so deep links and notifications keep working. The header carries the
state and priority pills, the Tasks breadcrumb, the deadline in words and
the engine's verbs, Finish through dossiq's completion route. Overview holds
the description, checklist, appointments and notes; History the engine's
trail; the side column the task facts, the case card, the waiting-case card
and the history. Needs the nextcloud-vue record-source verbs, checklist
widget and pill values.

Assisted-by: ClaudeCode:claude-opus-5-5

* docs(tasks): name the spec on the history leaf's open switch

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(case): write the picked role to the Add party form

RoleTypePicker emitted `select`, but CnFormDialog mounts a form-field
widget listening for `input` and `update:modelValue` only, so a picked
role never reached the form. Emit update:modelValue and read the
modelValue the dialog binds.

Assisted-by: ClaudeCode:claude-opus-5-5

* refactor(settings): declare eight plain admin forms as settings fields

Menu structure, queue urgency, mandate and consultation behaviour, the
review report switches, the payment callback secret, the store registry and
the archival reminder interval become CnSettingsFieldsSection declarations
in adminFields.js. Their eight tab components, archivalApi.js and
menuStructureSetting.js go. Section ids, setting keys and endpoints stay as
they were, so stored app config reads unchanged.

Needs the nextcloud-vue release with the settings field kinds of
feature/dq-abstract-l2-settings-fields (radio, status, action, visibleWhen,
trueValue/falseValue, setKey, integer, detailedDescription, autoSave,
savedMessage).

Assisted-by: ClaudeCode:claude-opus-5-5

* refactor(lists): deadline, export and personal queue as library config

- Deadline columns use the library `date` cell with `due` and an
  `upcomingLabel`; DeadlineCountdownCell and the dead
  `deadlineCountdown()` helper are gone (daysUntilDeadline and isOverdue
  stay for their other callers).
- The Cases page uses CnIndexPage's own Export menu (`allowExport`, case
  schema `configuration.exportable`, case 1.46.0); CaseListExportAction
  and caseExportHelpers.js are gone.
- /my-queue is a dashboard page with one `work-queue` widget, a
  breadcrumb and an `open-modal` CnActionDialog header action;
  PersonalQueueView and planItemDialog.js are gone.
- Specs ported to manifest assertions; e2e reads the library test id.

Assisted-by: ClaudeCode:claude-opus-5-5

* wip(cells): library cells via manifest config

Assisted-by: ClaudeCode:claude-opus-5-5

* test(settings): tidy imports after the forms moved to declarations

Assisted-by: ClaudeCode:claude-opus-5-5

* refactor(tasks): tasks, task leaves and decidiq leaves on library widgets

The case's Tasks section is the library task widget (`variant: focus`): the
first open task with its number, handler, deadline, lock, candidates, form and
verbs, completing through dossiq's route and claiming only where dossiq's
capability probe says the engine can. The simple profile's task table
completes and claims the same way. CaseTaskPane, TaskFormFields and
caseTaskPaneHelpers are gone.

The task page's notes, appointments and history are the library cards on the
task's engine endpoints (`source: flow-task`), and the case a flow run waits
on is a library notice linking it. TaskNotesLeaf, TaskEventsLeaf,
TaskAuditLeaf, TaskWaitingCaseSection and flowTaskHelpers are gone.

My work's task list is the library task widget (`scope: assigned`).
Substituted work shows only in the work queue (Ruben, 20 Oct), so
MyWorkWidget, its toggle, the SubstitutedWork card and the substitution
helpers behind them are gone; the e2e now finds the covered case in the
queue.

The case's Decisions tab and the document dialog's approval chain go through
the library integration widget, which mounts decidiq's leaves and says
"Decidiq not available" without them. BesluitvormingLeafTab and
ApprovalChainLeafTab and their registry keys are gone.

Needs the nextcloud-vue branch feature/dq-abstract-l3-tasks.

Assisted-by: ClaudeCode:claude-opus-5-5

* refactor(settings): declare KCC, AI, background account and digest forms

The KCC bridge, AI-assisted processing and background service account
admin sections, and the daily digest in the user settings dialog, become
CnSettingsFieldsSection declarations. Their four components, the KCC
trigger-word util and the AI and digest service calls they alone used go;
the AI switch-state and trigger-word specs move to the declaration spec
and the nextcloud-vue field-kind spec. The KCC e2e finds its fields by
label and waits for the section's PUT.

Needs the nextcloud-vue release with feature/dq-abstract-l2-settings-fields.

Assisted-by: ClaudeCode:claude-opus-5-5

* refactor(settings): declare the shared mailbox as settings fields

The shared mailbox section becomes a CnSettingsFieldsSection declaration
over its three endpoints: the intake keys on /api/settings/email, matching
on the instance matching endpoint (which still checks the pattern), and
the sender and allowed recipients on dossiq's own settings. Those three
were posted to the mailbox endpoint, which drops them, so they never
saved; now they do. The explanations fold behind one button as before.
EmailSettings.vue goes.

Needs the nextcloud-vue release with feature/dq-abstract-l2-settings-fields.

Assisted-by: ClaudeCode:claude-opus-5-5

* refactor(settings): the register and schema picker is CnRegisterMapping

The Configuratie section becomes the library's CnRegisterMapping over one
group: the `register` key and a `<kind>_schema` key per kind, the ids the
app config has always held. AdminRoot reads and saves them through
dossiq's settings; Settings.vue goes. Schema titles keep reading in the
reader's language through the cnTranslate the page already provides
(nextcloud-vue feature/dq-abstract-l2-settings-fields).

Assisted-by: ClaudeCode:claude-opus-5-5

* refactor(forms): bulk acts, field filters, party and role fields, templates, file set and rebind fields on the library

The bulk dialogs mount CnBulkJobProgress and CnSelectionScope with the case
wording; the Cases page declares relatedFieldFilters; requester and
represented party are the form's party widget with manifest sources; the
Role field is the form's resource widget scoped to the case type; the search
plan and the close form use CnTemplateSelect; the delivered set's items are
a field-list widget whose Compare opens DocumentCompareDialog; the rebind
fields are CnSettingsField; the party kinds editor uses CnOrderedListField.

Deletes BulkJobProgress, BulkSelectionScope, CaseTypeFieldFilters,
PartySearchField, partySources, RoleTypePicker, roleTypeOptions,
RecordTemplateSelect, TemplatePicker, FileSetItems and RebindPropertyField,
and the InitiatorPicker, RoleTypePicker, CaseTypeFieldFilters and
file-set-items registry keys. Needs @conduction/nextcloud-vue with
feature/dq-abstract-l5-forms.

Assisted-by: ClaudeCode:claude-opus-5-5

* test(queue): the queue widget names no source

Assisted-by: ClaudeCode:claude-opus-5-5

* test(tasks): my-work is the task widget; l10n tokens are not text

The my-work assertion reads the library `tasks` type. The l10n check no
longer collects a manifest value that is a record token (`@object.<field>`,
the notice link's label) as a string to translate, and the catalogues are
back in canonical order.

Assisted-by: ClaudeCode:claude-opus-5-5

* refactor(cells): case list cells as library config, app cell registry gone

- Days in status: library `number` cell with unit and the stuck flag.
- Lens columns: no widget; the library cell reads the withheld marker.
- Incomplete / on hold / draft: Cases `rowIndicators` (held via
  `after: "now"`), the state column is gone.
- Status: library `badge` with `resolve` on the status type colour.
- Priority: library `badge` with `valueField: priority`, a colorMap and
  the "set by hand" `titleWhen` tooltip.
- Unread: column gone, `showUnreadRows: true` on the Cases page.
- Store: `type: "store"` (CnStorePage) with the built-in items as config;
  StoreGallery is gone.
- All seven cells, src/services/cellWidgets.js and the CnAppRoot
  `cellWidgets` prop are deleted; specs ported to manifest and library
  cell assertions.

Needs the nextcloud-vue release carrying feature/dq-abstract-l1-config
(number cell, withheld default, row indicator dates, titleWhen,
valueField, showUnreadRows, translated store items).

Assisted-by: ClaudeCode:claude-opus-5-5

* docs(tasks): the simple My tasks note names the library widget on both profiles

Assisted-by: ClaudeCode:claude-opus-5-5

* refactor(cases): the deleted cases list is an endpoint index

CasesDeleted becomes `type: "index"` over dossiq's /api/cases/deleted
(entitySource `endpoint`). Restore and destroy are row actions that open
CnActionDialog, which posts and shows the server's refusal sentence; the
list reads its rows again after an act. The refusal sentences move to the
server (CaseRecycleController answers `message` beside `code`), and each
row carries `windowNote` ("12 days left", "the window has passed") for the
line under its date. DeletedCasesView.vue and caseRecycleApi.js go; the
lens spec and the e2e follow the declaration.

Needs the nextcloud-vue release with feature/dq-abstract-l2-index-row-modal
(row actions of type open-modal, refresh on cn:page:refresh).

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(forms): lint, l10n order and four specs on the forms branch

The field-list Compare row action compares against `@row.originalRef`, the
token the manifest schema accepts, so the built simple manifest validates.
The registry orphan check counts a field-list row action's `dialog` as a
way to reach a registry dialog. The party widget count is the nine requester
overrides plus the represented party there were before, sorted. The contact
on a contact moment is asked through the party widget wherever a form asks
it; the Log contact forms preset it. Import order and l10n catalogue order
fixed.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(l10n): English entries for the deleted cases dialogs

feature/dq-abstract-l2-index-pages added the restore and destroy dialog
strings and the days-left plural to the Dutch catalogue only. The JS
catalogues are rebuilt with scripts/build-l10n-js.js.

Assisted-by: ClaudeCode:claude-opus-5-5

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

Syncs the vendored manifest schema to the installed one for the lag check.

Assisted-by: ClaudeCode:claude-opus-5-5

* test: the ratchet and the team queue follow the L1 and L2 page moves

feature/dq-abstract-l1-config makes Store a library store page and drops the
team queue's unread column for unread rows; feature/dq-abstract-l2-index-pages
makes CasesDeleted an endpoint index. Neither branch moved the custom page
ratchet or the team queue spec.

Assisted-by: ClaudeCode:claude-opus-5-5

* style(cases): wrap the retention refusal sentence under 150 characters

feature/dq-abstract-l2-index-pages added the line at 168 characters, which
phpcs refuses.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix: batch 22 CI reds (phpmd, l10n order, deleted cases saved views)

- CaseRecycleController takes the translator as a tenth collaborator for the
  window note and refusal sentences (feature/dq-abstract-l2-index-pages);
  the coupling and parameter-count rules are suppressed with the reason, as
  the repository's other wide controllers do.
- l10n/nl.json is put back in canonical order (npm run test:l10n:write) and
  nl.js rebuilt.
- CasesDeleted joins the index pages without a filter sidebar in the saved
  views spec: /api/cases/deleted takes only limit and offset, so a view saved
  there would hold nothing.

Assisted-by: ClaudeCode:claude-opus-5-5
* fix(case): write the picked role to the Add party form

RoleTypePicker emitted `select`, but CnFormDialog mounts a form-field
widget listening for `input` and `update:modelValue` only, so a picked
role never reached the form. Emit update:modelValue and read the
modelValue the dialog binds.

Assisted-by: ClaudeCode:claude-opus-5-5

* refactor(cases): retire dead pages, components and registry keys

Removes code nothing mounts: LocationPicker (and the leaflet and
leaflet-draw dependencies it alone used), PropertyDefinitionFields with
its vocabulary and concept-scheme services, InspectionPanel, and the
DeelzaakList and DeelzaakDetail pages. Drops the unused registry keys
BesluitvormingLeafTab, CaseNotesTab and CaseTimelineTab; their
components stay under the keys that mount them. registerNavCardGrid
goes because nextcloud-vue 2.114.0 resolves nav-card-grid through its
built-in widget fallback.

The old sub-case addresses redirect: /cases/:id/deelzaken opens the
parent on its Related tab, /cases/:parentId/deelzaken/:id opens the
sub-case. E2e tests that opened the removed pages or targeted the
#pd-add-* ids are parked with test.fixme and a reason.

Assisted-by: ClaudeCode:claude-opus-5-5

* test(case types): drive the field vocabulary e2e through the manifest field form

The two specs targeted #pd-add-* ids from a component no page mounted.
They now open the case-type-fields-editor form. The form gains the
list entry type (items) for an array field, so a set-valued answer can
be declared there.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(cases): create sub-cases and roll them up on the case page

The Sub-cases section on a case's Related tab declares a createAction
that opens DeelzaakCreateModal for the case on screen, shown only on an
open case that is not itself a sub-case, and a summary line that rolls
up how many sub-cases are completed. Both structure profiles carry it.

DeelzaakCreateModal reads the parent and its case type itself when it is
opened from the manifest, says why a closed case or a sub-case cannot
take one, and refreshes the page and closes after a create. It is a
registered modal again, so it leaves the unimported list.

The deelzaak-support e2e scenarios for create, roll-up and the parent
link run on the case page; the delete-from-list tests go with the page.

Needs the nextcloud-vue object-list createAction and summary keys
(feature/dq-abstract-l8-dead there); on the current release the section
renders as before, without the action and the roll-up.

Assisted-by: ClaudeCode:claude-opus-5-5

* refactor(forms): bulk acts, field filters, party and role fields, templates, file set and rebind fields on the library

The bulk dialogs mount CnBulkJobProgress and CnSelectionScope with the case
wording; the Cases page declares relatedFieldFilters; requester and
represented party are the form's party widget with manifest sources; the
Role field is the form's resource widget scoped to the case type; the search
plan and the close form use CnTemplateSelect; the delivered set's items are
a field-list widget whose Compare opens DocumentCompareDialog; the rebind
fields are CnSettingsField; the party kinds editor uses CnOrderedListField.

Deletes BulkJobProgress, BulkSelectionScope, CaseTypeFieldFilters,
PartySearchField, partySources, RoleTypePicker, roleTypeOptions,
RecordTemplateSelect, TemplatePicker, FileSetItems and RebindPropertyField,
and the InitiatorPicker, RoleTypePicker, CaseTypeFieldFilters and
file-set-items registry keys. Needs @conduction/nextcloud-vue with
feature/dq-abstract-l5-forms.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(substitution): substitution admin lives only in the admin settings

Ruben's decision (20 Oct 2026): one place. The app page /substitution-admin,
its menu entry and its registry key go; the Substitution tab of the admin
settings keeps the console. The old address goes on to that tab
(RETIRED_ROUTES), and the e2e opens the console there.

Merges feature/dq-abstract-l8-dead for its RETIRED_ROUTES entries.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(cases): the Sub-cases section says why it offers no Create sub-case

A closed case and a sub-case get the createAction hiddenNote in the
action's place, the sentence the old list page showed.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(case): the Proposals tab stands in the case strip while proposals wait

Board PtVoorstelBeoordelen draws the case with a Voorstellen tab and its
count in the strip. The simple profile kept Proposals under More at all
times. It now sits after Contact, shows only while portaliq's queue route
answers proposals for this case, and counts them from the same route, so a
reader who may not review, a case without waiting proposals and an
instance without portaliq keep today's strip.

Needs the nextcloud-vue release that resolves @objectid in an endpoint
condition and counts an endpoint's list (feat/tab-endpoint-visibility-and-count).

Assisted-by: ClaudeCode:claude-opus-5-5

* refactor(lifecycle): the act fields are library input declarations

CaseLifecycleMenuDialog draws the chosen act's inputs with the library's
CnTransitionInputFields and decides whether it may confirm, and what it
sends, with readTransitionInputs. What each act asks is declared in
src/utils/caseActInputs.js: the result template (applied to the reason,
never over typed text), the case type's pause reasons with Other falling
back to the reason text, the reason (one line beside the days on suspend,
a text area elsewhere, label above it tied by id), the result picker, the
return date, and the days with the legal basis of the picked reason as
helper and the comes-back-on preview. The archive summary, retention facts,
read-only notice, dialog title and confirm icon are the archive act's
declarations instead of act.id branches.

The dialog's reason, result, until, days and pause reason fields become one
values object. Only what an act asks now is sent, so text typed before a
declared pause reason was picked instead of Other is no longer posted.

Deletes CaseActFields.vue and pauseReasonChoice.js; their specs are ported
to the declarations and to the library's own specs. Needs
@conduction/nextcloud-vue with feature/dq-abstract-l5b-actfields.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(forms): lint, l10n order and four specs on the forms branch

The field-list Compare row action compares against `@row.originalRef`, the
token the manifest schema accepts, so the built simple manifest validates.
The registry orphan check counts a field-list row action's `dialog` as a
way to reach a registry dialog. The party widget count is the nine requester
overrides plus the represented party there were before, sorted. The contact
on a contact moment is asked through the party widget wherever a form asks
it; the Log contact forms preset it. Import order and l10n catalogue order
fixed.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(case): Proposals follows the DqZaak tabs and goes under More

Where PtVoorstelBeoordelen and DqZaak draw a different case strip, the
dossiq board wins: Overview, Documents, Contact, Tasks and Case history
keep their five places, so Taken stays in the strip. Proposals now
stands after Case history and, while proposals wait, is listed under
More with their count.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(cases): a Suspended lens on the case list

DqZaken, DqZakenSorteren, DqFilterBeheren and DqNieuweZaak draw the
case list's view strip ending in "Opgeschort": the open cases whose term
stands still. A pause under Awb 4:5 lives on the case's term row
(deadlineInstance status paused, written by DeadlinePauseService), not on
the case, so the lens asks OpenRegister for the case through the term
rows that point at it (_related, query-related-schema-rows), the way the
organisations list asks for its open cases.

The simple structure shows it as the sixth counted view after Woo
requests, as the boards draw the strip; the full structure keeps its
four pills and lists it last behind the overflow chip.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(landing): name the My work / My team switch "Display"

DqMijnWerk and DqMijnTeam label the landing page's view switch
"Weergave"; the page called it "Whose work". The label is the switch's
accessible group name and reads "Weergave" in Dutch.

Assisted-by: ClaudeCode:claude-opus-5-5

* test(registry): the sub-case create modal is mounted again

feature/dq-abstract-l2-substitution-admin (2479605) mounts DeelzaakCreateModal
from the Sub-cases section's create action and empties KNOWN_UNIMPORTED; the
merge kept development's entry, so the branch's change is restored.

Assisted-by: ClaudeCode:claude-opus-5-5

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

Syncs the vendored manifest schema to the installed one for the lag check.

Assisted-by: ClaudeCode:claude-opus-5-5

* test: the substitution admin page leaves the menu and the custom page count

feature/dq-abstract-l2-substitution-admin moves substitution admin into the
admin settings, which drops its menu entry and its custom page; neither spec
had moved with it.

Assisted-by: ClaudeCode:claude-opus-5-5

* docs(settings): name the spec on the background service group

feature/dq-abstract-l2-settings-forms-d added the computed without an @SPEC
anchor (hydra gate 16).

Assisted-by: ClaudeCode:claude-opus-5-5

* docs(cases): name the spec on the sub-case types the create form offers

feature/dq-abstract-l2-substitution-admin (2479605) changed the computed
without an @SPEC anchor (hydra gate 16).

Assisted-by: ClaudeCode:claude-opus-5-5

* test: page and tab counts follow substitution admin and the proposals tab

Compared with development, one page and one menu entry move and one tab
changes kind; nothing a user has is lost:
- SubstitutionAdmin (menu: Substitutions & reassignment) moves into the
  admin settings; /substitution-admin goes there (54dcb5d).
- Proposals stops being a fixed overflow tab and shows while proposals wait
  on the case, with portaliq installed (feat/case-proposals-tab).
The page counts drop by one (58 -> 57, 75 -> 74), and the domain-tab spec
leaves Proposals out of the case type domains it lists.

Assisted-by: ClaudeCode:claude-opus-5-5
* feat(cases): the assistant as a tab on the case page

DqZaakAssistent draws the assistant inside the case page, in the
AI-toezicht tab; it was reachable only through the launcher's floating
window. The simple case page gets an AI oversight tab holding the same
AI Chat Companion, embedded through nextcloud-vue's `ai-chat` widget,
with the case as its context and hermiq required. It waits under Acties
with the other panels and joins the strip while open. The hex and the
Assistant sidebar tab stay.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(cases): the AI oversight tab lists what the assistant did and the macros

DqZaakAssistent draws the AI-toezicht tab with a table of the
assistant's suggestions on the case, a Macro's card and the chat. The
tab held only the chat. It now stacks an object list over hermiq's
advisory approvals for this case (moment, user, question, outcome,
model), the library macros widget over the case schema's macros, and
the chat. Both sources exist; dossiq does not yet report decisions to
hermiq and the case schema declares no macros, so both cards say so.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(cases): the assistant chat beside the AI oversight tab

DqZaakAssistent draws the chat in the side column beside the AI-toezicht
panel, not inside it. The tab gets an id, its panel stacks the activity
list and the macros, and the chat moves to the side column with `tabs:
["ai-oversight"]`, so it shows only while that tab is open. It follows
the four generic side cards, which keeps a case type's own side-card
list valid.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(cases): the case schema ships the board's two macros

DqZaakAssistent's Macro's card lists "Woo-verzoek afronden" and
"Opschorten voor aanvulling", and the case schema declared no macros.
It now declares both as actions with macro: true, bound by name to two
manual flows it declares: status to Besluit and the Publiceren task;
and the term suspended, then the handler asked to send the message
Aanvullende gegevens. Suspending needed a flow step, so a generic
dossiq.suspendTerm node runs the same pause the Termijn card registers,
under the case type's rules. Every changing step carries a label for
the card's last-run line.

Assisted-by: ClaudeCode:claude-opus-5-5

* chore(l10n): rebuild the browser catalogues for the AI oversight strings

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(cases): the AI oversight tab gives the side column to the assistant

DqZaakAssistent draws the Assistent card alone beside AI-toezicht, but
the four generic side cards showed beside every tab. They now declare
notTabs ["ai-oversight"] (nextcloud-vue side-card-not-on-tabs) and stand
aside while that tab is open.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(cases): AI-toezicht reads its records through hermiq

hermiq#1172 makes AI oversight records readable only through their
subject, so the AI-toezicht list reads GET
/apps/hermiq/api/ai-oversight/subject for the case (object-list
endpointSource) instead of the OpenRegister list. It names hermiq's new
unused status "Niet gebruikt" and carries the board's subtitle.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(cases): a case type chooses the macros its cases offer

The case schema declares its macros, so every case type offered every
one: Woo-verzoek afronden on an objection. caseType gets `macros`, the
keys its cases offer in order; the Woo request lists both; and the
Macro's card reads the list through nextcloud-vue macrosFrom. A case
type that lists none offers none.

Assisted-by: ClaudeCode:claude-opus-5-5

* test(architecture): the term suspension node asks nobody

The queue-source architecture test lists every flow node that may ask
a person. dossiq.suspendTerm registers a pause and asks nobody; in a
macro the askPerson step after it does the asking. Classified in the
allowlist with that reason.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(cases): move the case schema to 1.46.0 for its macros

The macros and their flows live in the case schema's configuration,
which OpenRegister's importer does not compare, so an instance that
already had 1.45.0 would skip them on import. The version moves and the
digest record is updated.

Assisted-by: ClaudeCode:claude-opus-5-5

* test(schemas): record the digest of the caseType macros fragment

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(cases): whoever may edit a case may run its macros

Ruben decided who may run the macros: whoever may edit the case. The
two macro actions had no rule of their own, so OpenRegister's default
decided. Each now carries the case's update rule, the same groups and
assignee match, and a test keeps the two equal.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(case): hand the loaded case to the attention strip

CaseBannerStack bound only objectId on CaseAttentionPanel. The strip reads
attentionMarkers and riskAssessment off objectData, so on the live case
page neither the markers nor the risk assessment were ever drawn.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(case): read the case title from objectData in the programme section

CaseProgrammeSection declared a prop `object`, but CnDetailWidgetHost
binds the loaded case as `objectData`, so the link dialog always got an
empty case title.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(case): stop the review panel shadowing the library document-review widget

dossiq registered DocumentReviewPanel under `document-review`, the key of
nextcloud-vue's library widget (CnDocumentReviewWidget). The app registry
resolves first, so the app key replaced the library widget for any page
of this app asking for it. Register the panel as
`document-relevance-review` and point the case Review tab at it. The
widget id `case-document-review` is unchanged, so saved arrangements and
the menu layout keep finding it.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(reports): shape the processing-time and process-mining figures on the server

Add GET /api/reports/processing-time?period&caseType&view&threshold. It
answers the blocks the processing-time dashboard drew from every case,
case type and status loaded into the browser: sla (+ slaSummary),
distribution (+ distributionTarget), trend, throughput, atRisk and
breakdown, each in the shape a chart, stat or object-table widget reads
through responsePath. Rows carry severity and status words beside the
colour keys. A case type filter matches id or slug, `truncated` says the
load hit its limit, and the reporting audience gate refuses everyone else
with its sentence. The unfiltered figures are handed to the OpenRegister
analytics leaf under the existing procest-sla-* keys.

/api/reports/process-mining accepts `period` (preset) and `view`, and
answers the summary, dwell, throughput, bottlenecks and byAssignee blocks
plus the clock-disclosing column labels beside the existing report.

The quarterly deadline report adds `rows`, the per-type figures as a list
named by case-type title, with the penalty total in euros.

The JS shaping specs (doorlooptijdHelpers, doorlooptijdChartShaping,
processMiningShaping, dwellTimeClock) are ported to PHPUnit.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(dashboards): processing time, process mining and deadline reports as library widgets

The three report dashboards are now manifest config over chart, stat and
object-table widgets on the server-shaped report endpoints; the dossiq
widget components, their mixins, the shaping modules, the doorlooptijd and
processMining stores and their API clients are deleted.

- Processing time: four charts (compliance by type, distribution, monthly
  trend, weekly throughput) on /api/reports/processing-time, one view
  each. The cases at risk and the deadline list of one case type are the
  same at-risk object-table; the second is narrowed to its case type by
  configuration (id or slug), not by "title contains woo". Severity is a
  translated enum badge, so text sits beside the colour, and rows open the
  case. The breakdown is a sortable table that names each status band.
- Process mining: four stat tiles, a dwell chart, a throughput chart and
  two tables on /api/reports/process-mining views. The duration headings
  and the dwell series name come from the report's `labels`, so they still
  say which clock the numbers are on; the hint sentences are subtitles.
- Deadline reports: the quarterly report is an object-table over the
  report's `rows` with CSV export, percent and euro formats and English
  column text; the annual audit is three stat tiles. Quarter and year are
  page filters.
- The mail parties report is nextcloud-vue's CnCountTablesReport,
  configured in mailPartiesReport.js.
- The substituted-work card on My work is dropped (Ruben, 20 Oct): the
  work queue carries covered work.

The annual audit summary adds totalEuros and warningCount; the at-risk
severity reads "at risk" from fourteen days left, as the deadline list
did. Depends on the unreleased nextcloud-vue branch
feature/dq-abstract-l6-dash (endpoint notices, exportCsv, labelPath,
namePath, quarter/year page filters, CnCountTablesReport).

Assisted-by: ClaudeCode:claude-opus-5-5

* docs(audit): the processing-time calculator's date buckets are not a term

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(reports): the document review reports page as dashboard config, read on request

The reports page is a dashboard: a date range, the throughput table on
/api/woo/reports/throughput with `loadOn: "request"`, the mail parties
report as a registered CnCountTablesReport widget, and the CSV as a
header link carrying the page's period. The throughput endpoint records
every read on the audit trail of the cases it counts, so the table reads
only when the reader presses Show: a plain visit records nothing.

Each half shows only while its report is offered, read from the new
GET /api/reports/offered (the same answer the menu reads). The throughput
endpoint reads a moment from the date range as the tenant's calendar day.
The custom WooReportsView is deleted.

Assisted-by: ClaudeCode:claude-opus-5-5

* refactor(case): the record widgets are library config (DQ-ABSTRACT L4)

The case page's record widgets are declared in the manifest over
nextcloud-vue widgets, and dossiq's sixteen copies are gone:

- banner row: a `sections` widget over a `banner` (archived, on
  @self.archived), a `status-explainer` (/available-transitions) and an
  `attention-flag` (/attention). New-since-you-looked is counted on the
  tabs (`badgeSource: readState`, files and notes) and cleared on open;
  CnDetailPage keeps the read stamp and Mark as unread.
- followers: the header popover only (Ruben, 20 Oct 2026); the People tab
  section and the simple profile's Followers card are gone.
- location map (`map`), parties (`parties`), contact moments in the
  customer record (`record-list` with row actions and an "Also on" line),
  family plan (`sections`: review-due banner and grouped cards),
  related cases (`related` with endpoint sections, the parent and Plan
  follow-up), programme and work instruction (`facts`), Notes tab (the
  notes leaf with mentionUrl and the push action), redaction page
  (`redaction` page type), First today (`banner` naming the first row).
- Widget ids users may have stored are unchanged (family-plan,
  case-pipelinq-contact-moments and the rest); L7 renames them.
- FileContactMomentDialog and LinkProgrammeDialog are registry modals the
  widgets open; both ask the page to refresh on success.
- Helpers only the deleted components used are pruned; the reading moved
  to nextcloud-vue (objectParties, statusDeclaration, fieldRules).
- Specs: the declarations are asserted here
  (recordPageLibraryWidgets and the rewritten specs); the behaviour specs
  moved to the nextcloud-vue widgets. The DQ-BUGS specs for the attention
  strip and the programme title assert the fixed declarations.
- check:manifest treats a page type only the newer schema lists as a
  library lag; the vendored schema is 2.137.0.

Depends on nextcloud-vue feature/dq-abstract-l4-record and
feature/dq-abstract-l4-record-b (unreleased): with the installed 2.114.0
one spec, the note push, waits for that release.

Assisted-by: ClaudeCode:claude-opus-5-5

* test(manifest): tell a value or branch lag from a defect; vendor schema 2.138.0

The reports page's header link drops its `_note`, which $defs/action
refuses. The validator now also asks the vendored newer schema about a
path when the installed one refuses a value or an anyOf branch there (the
generated quarter and year page filters): with nothing refused near that
path in the newer schema, the error is the library lagging. The vendored
schema is the nextcloud-vue branch's 2.138.0, ahead of the installed
2.136.0 until that release lands.

Assisted-by: ClaudeCode:claude-opus-5-5

* chore(manifest): record the deadline page's dependency on nextcloud-vue schema 2.139.0; vendor it

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(example-set): Pieter Jansen owns a saved view on the case list

The saved-view boards (DqFilterBeheren, -Bewerken, -Delen, -InMenu,
-Verwijderen) draw the case list's side panel on Weergaven with the
reader's own views and their row menu: edit, share, add to the menu,
delete. That menu exists only on a view the reader owns, and the example
data gave the boards' handler none.

The Zuiddrecht set now gives him "Termijn deze week": his open cases with
a deadline within seven days, sorted on deadline, scoped to the case
list. Its conditions are the shipped default view "Mine, due this week",
so it names no status or case type id. Needs OpenRegister's example-set
views and Pieter in the cast (openregister r21/cast-pieter). Sharing and
the menu entry are not seeded: an example-set view writes name,
description, owner, isPublic, query, presentation and alert only.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(enforcement): the advice page follows board DqHandhaving

The enforcement advice page drew a Follow toggle the board does not, put
the recommendation and the deviation as full-width data panels where the
board has side cards, and titled its history card Geschiedenis where the
board and the other record pages say Historie.

- follow: false on LhsRecommendationDetail
- Aanbeveling and Afwijking en definitief become side cards with every
  field the panels showed; deviating stays the header's edit action
- the main column holds the matrix and the violation
- the history card uses "Record history" (nl Historie)

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(organisations): the organisation page's history card reads Historie

Board DqTenant titles the side card Historie, as the contact and
organisation record pages do; the organisation page used "History",
which reads Geschiedenis in Dutch.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(procurement): contracts, suppliers and portal accounts share one set of tabs

Board DqContracten draws Contracten | Leveranciers | Portaalaccounts.
The procurement pages now link the supplier portal accounts as a third
tab, shown to an admin only and without a count there (the library still
requests a hidden tab's count). The accounts page gets the same three
tabs; it stays admin-only on its own.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(lists): task and case type lists read as boards DqTaken and DqZaaktypen

The simple profile's task list searched "Zoeken..." and headed its due
column Termijn; board DqTaken says "Zoek op taak of zaaknummer" and
Deadline. The case type list searched "Zoeken in deze lijst"; board
DqZaaktypen says "Zoek een zaaktype".

Assisted-by: ClaudeCode:claude-opus-5-5

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

Syncs the vendored manifest schema to the installed one for the lag check.

Assisted-by: ClaudeCode:claude-opus-5-5

* chore(register): move the register to 0.20.63 and bump the app version

The macros branch moves the case schema to 1.47.0, caseType to 1.28.0 and the
Woo-verzoek case type to 1.4.12, so the register moves once over
development's 0.20.62.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(l10n): put the merged catalogues back in canonical order

The batch's merges appended keys out of order; npm run test:l10n:write sorts
them (no key added or dropped) and the JS catalogues are rebuilt.

Assisted-by: ClaudeCode:claude-opus-5-5

* test(reports): drop the imports only the moved admin switches used

Assisted-by: ClaudeCode:claude-opus-5-5

* test(landing): the simple dashboard may have no slots at all

feature/dq-abstract-l4-record moves First today onto the library banner, the
last slot the simple dashboard named, so its slots map can be absent.

Assisted-by: ClaudeCode:claude-opus-5-5

* test(registry): the sweep floor follows the retired record and report components

Development held 104 registry entries; this batch retires 28 (the L4 record
widgets and the L6 report dashboards, now library config) and adds 3, so 79
remain. Every name the manifests still use resolves (the registers-every-name
case passes), and none of the retired keys is a type, component, handler,
widget or target in any manifest.

Assisted-by: ClaudeCode:claude-opus-5-5
The Gemachtigde role of kenteken-henk was looked up by name alone, which
matches the generic role type and the vth package's two per-case-type ones,
so the installer refused the set wherever vth was installed. It now also
asks genericRole "gemachtigde", which only the generic role type holds. The
Woo decision type lookup names "WOO-besluit" as well as its case type.

A new test resolves every dossiq lookup of the set, chained lookups
included, against the rows the register, register.d, the packages and the
repair seeds ship, and fails when one matches more than one row.

Assisted-by: ClaudeCode:claude-opus-5-5
The inspectionChecklistRun schema requires case, and the run in progress
shipped without one, so OpenRegister refused the whole Zuiddrecht set on
install. The run belongs to the terrace roofing case Esra handles. A test
now checks every dossiq object of the set against its schema's required
properties.

Assisted-by: ClaudeCode:claude-opus-5-5
* feat(reports): shape the processing-time and process-mining figures on the server

Add GET /api/reports/processing-time?period&caseType&view&threshold. It
answers the blocks the processing-time dashboard drew from every case,
case type and status loaded into the browser: sla (+ slaSummary),
distribution (+ distributionTarget), trend, throughput, atRisk and
breakdown, each in the shape a chart, stat or object-table widget reads
through responsePath. Rows carry severity and status words beside the
colour keys. A case type filter matches id or slug, `truncated` says the
load hit its limit, and the reporting audience gate refuses everyone else
with its sentence. The unfiltered figures are handed to the OpenRegister
analytics leaf under the existing procest-sla-* keys.

/api/reports/process-mining accepts `period` (preset) and `view`, and
answers the summary, dwell, throughput, bottlenecks and byAssignee blocks
plus the clock-disclosing column labels beside the existing report.

The quarterly deadline report adds `rows`, the per-type figures as a list
named by case-type title, with the penalty total in euros.

The JS shaping specs (doorlooptijdHelpers, doorlooptijdChartShaping,
processMiningShaping, dwellTimeClock) are ported to PHPUnit.

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(dashboards): processing time, process mining and deadline reports as library widgets

The three report dashboards are now manifest config over chart, stat and
object-table widgets on the server-shaped report endpoints; the dossiq
widget components, their mixins, the shaping modules, the doorlooptijd and
processMining stores and their API clients are deleted.

- Processing time: four charts (compliance by type, distribution, monthly
  trend, weekly throughput) on /api/reports/processing-time, one view
  each. The cases at risk and the deadline list of one case type are the
  same at-risk object-table; the second is narrowed to its case type by
  configuration (id or slug), not by "title contains woo". Severity is a
  translated enum badge, so text sits beside the colour, and rows open the
  case. The breakdown is a sortable table that names each status band.
- Process mining: four stat tiles, a dwell chart, a throughput chart and
  two tables on /api/reports/process-mining views. The duration headings
  and the dwell series name come from the report's `labels`, so they still
  say which clock the numbers are on; the hint sentences are subtitles.
- Deadline reports: the quarterly report is an object-table over the
  report's `rows` with CSV export, percent and euro formats and English
  column text; the annual audit is three stat tiles. Quarter and year are
  page filters.
- The mail parties report is nextcloud-vue's CnCountTablesReport,
  configured in mailPartiesReport.js.
- The substituted-work card on My work is dropped (Ruben, 20 Oct): the
  work queue carries covered work.

The annual audit summary adds totalEuros and warningCount; the at-risk
severity reads "at risk" from fourteen days left, as the deadline list
did. Depends on the unreleased nextcloud-vue branch
feature/dq-abstract-l6-dash (endpoint notices, exportCsv, labelPath,
namePath, quarter/year page filters, CnCountTablesReport).

Assisted-by: ClaudeCode:claude-opus-5-5

* docs(audit): the processing-time calculator's date buckets are not a term

Assisted-by: ClaudeCode:claude-opus-5-5

* feat(rename): UI-facing names say what they do, with aliases for the old ones

What the UI calls and names no longer carries a procedure word, and
nobody who used the earlier name loses anything.

- Routes: /api/checklists, /api/cases/{id}/checklist-results,
  /api/deadlines/{dashboard/kpi, reports/quarterly|annual|first-response|
  open-workload-age, definitions, calendar, instances/{id}/pause|resume|
  extend|complete}, /api/payments/callback and
  /api/document-review/reports/throughput. Every old URL stays routed to
  a <method>LegacyPath() delegate with the same auth posture (AppHost
  rejects duplicate route names); the payment callback's old URL is
  permanent because the payment provider is configured with it.
- Deadline reports carry generic keys beside the old ones for one
  release (total, withinTermPct, penaltyTotalCents, period, year, ...).
- Processing-time series publish under dossiq-sla-* and procest-sla-*.
- Widget ids dt-* -> processing-time-*, td-* -> deadline-report-*,
  family-plan -> goal-plan, case-pipelinq-contact-moments ->
  linked-contact-moments; WooReports -> DocumentReviewReports at
  /document-review-reports, /woo-reports redirects. RewriteRenamedLayoutIds
  rewrites them in stored buildiq arrangements.
- Document review report settings on generic keys, read with a fallback
  to the old keys; CarryRenamedSettingKeys copies them over.
- Settings files renamed (DeadlineDefinitionsTab, DeadlineDefinitionEditor,
  DelegationMatrixTab, DelegationImportPanel, DelegationMatrixTable,
  RoleAssignmentsTable, RoleTreeManager, ChecklistTemplatesTab). The role
  tree is CnTreeView; the checklist template editor is CnFormDialog with
  the items as sub-objects, without the Dutch placeholder.
- NC dashboard ids procest_* stay frozen.

Assisted-by: ClaudeCode:claude-opus-5-5

* test(rename): role tree, renamed UI names and the refused checklist save

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(rename): lint on the renamed files; tree update event and button variants

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(rename): phpmd and eslint on the renamed settings and reporting files

Assisted-by: ClaudeCode:claude-opus-5-5

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

Syncs the vendored manifest schema to the installed one for the lag check.

Assisted-by: ClaudeCode:claude-opus-5-5

* test(rename): the reports page is a library dashboard

development (batch 24) made the reports page a dashboard with no app view,
so the renamed page is checked by its type and no reports view is registered.

Assisted-by: ClaudeCode:claude-opus-5-5

* fix(rename): hydra findings of the rename batch

- The fifteen deprecated path aliases declare @contract exclude, as the
  existing StUF aliases do: each calls the canonical method its tests cover
  (gate 25).
- The role tree's roles watcher names its spec (gate 16).
- The app version moves so the two new repair steps run on upgrade (gate 110).

Assisted-by: ClaudeCode:claude-opus-5-5
OpenRegister refuses a view alert without recipients, so the Zuiddrecht
install skipped zoek-lindelaan and DqZoekresultaten had no saved search.
Thomas, the view's owner, is now its one recipient: since
openregister#4685 only the owner or an administrator may name others.

Assisted-by: ClaudeCode:claude-opus-5-5

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant