Repository navigation
feat(ai): the assistant panel on the case renders AI features, refusals and the report group - #3648
Merged
Merged
Conversation
Twelve schemas declare a curated x-openregister-mcp block, so OpenRegister derives 20 read tools (dossiq.case.search, dossiq.statusRecord.search, ...). The hand-written DossiqToolProvider and its helpers are deleted, so nothing shadows the derived tools. OpenRegister RBAC is the only gate. Archives dossiq-mcp-adoption; live tasks 4.1-4.4 stay owed (decision 139).
A change outside properties/required/authorization only reaches an instance when the schema version moves (SchemaVersionFloorTest). Digests regenerated.
…nd workload figures First IMcpScannableServices opt-in (DossiqScannableServices, registered under OpenRegister's per-app alias). Curated #[McpTool] reads: getDeadlineDashboard, getDoorlooptijdMetrics, getKpiOverview, getWorkload (ReportingTools), listAvailableTransitions (CaseTools) and listOverdueComplaints on ComplaintService, a projection that never carries the complainant. Each tool runs the gate its controller runs, then calls the owning service.
…ct; screens.md for hermiq-ai-tooling
…creens.md for the touched specs and the archived change
# Conflicts: # lib/Settings/dossiq_register.json # lib/Settings/register.d/60-termijnbewaking.json # tests/schemas/schema-version-digests.json
# Conflicts: # lib/Settings/dossiq_register.json # lib/Settings/register.d/60-termijnbewaking.json # tests/schemas/schema-version-digests.json
…them without a grant Every curated read (deadline dashboard, lead times, KPIs, workload, available transitions, overdue complaints) now declares reach: 'user'. Without it a two-segment id resolves to external and needs an explicit grant. Needs openregister build/dep-dossiq-mcptool-reach on the instance first.
… and work its terms Curated write tools, each on the service its controller calls: transitionCase (StatusTransitionService, guards and status record intact), reassignCase (new CaseAssignmentService::reassign, coordinators only, the receiver is notified), completeTask (the engine's complete verb), extendDeadline/pauseDeadline/resumeDeadline (new TermTools: the term is read and its CASE must be changeable by the caller before the term services run). Refusals come back as envelopes with the service's own rule and sentence. WIP: AppointmentTools and BeschikkingTools are listed but not written yet.
…chikking AppointmentTools (scheduleAppointment, cancelAppointment; external reach: the resident is told) run AppointmentController's case checks and call AppointmentService. BeschikkingTools offers draftBeschikking only, through BeschikkingService::compose(); approving, signing and sending stay with the caseworker. A test holds the tool classes off ObjectService. hermiq-ai-tooling 3.1-3.3 ticked; the spec names the services as found.
# Conflicts: # lib/Settings/register.d/60-termijnbewaking.json # tests/schemas/schema-version-digests.json
# Conflicts: # .debt-baseline.json # appinfo/info.xml # lib/Settings/dossiq_register.json # lib/Settings/register.d/60-termijnbewaking.json # tests/schemas/schema-version-digests.json
# Conflicts: # lib/Settings/register.d/60-termijnbewaking.json # tests/schemas/schema-version-digests.json
…duleAppointment (phpmd finds nothing without it)
…s no approval floor Decision 176 (Q-dossiq-L6-2): REQ-MCP-206 now says hermiq decides which AI writes wait for a person. dossiq declares readOnlyHint: false and a scope on every write so hermiq can gate it, and ships no grant or waiver. CHANGELOG entry for the curated tools (task 4.3).
…siq.fileCase dossiq.fileCase carries the citizenIntake mark with scope and action create, which is how hermiq now recognises an intake tool (decision 177). It writes through the create form's own path: one save on the case schema as the caller, so the schema, the intake requirements, the duplicate policy and RBAC all run, and a refusal is thrown so hermiq hands the resident to a person. No intermediate intake object (decision 179). The six curated reads carry outsideAgent for hermiq's outbound surface. ai-features-on-the-case-consume-hermiq 5.1, 5.2; screens.md added.
caseType.aiFeatures (register fragment 43) maps hermiq's feature slug to case, intake or none, and holds no provider, model or residency. Register 0.20.25, info.xml bumped, en/nl strings. Ticks ai-features 1.1, and 1.2/2.1 with the files that already carry them.
…tick them with their files
…spelled-out alias (gate 57 could not see AppointmentTools)
…y in CaseIntakeFiling (phpmd), REQ-MCP-206 anchor follows the rewritten heading (gate 46)
…its group is read on the case A case type declares grouping as aiFeatures["report-similarity"]. hermiq's evaluation adds the report to a group, so asking twice would count the same resident twice: a created case is placed once, off the request, by ReportGroupingJob as the background service account, and the group id is kept on the case (case.reportGroupId, register fragment 44, register 0.20.26). GET /api/report-group reads the group as it now stands, with the count and the near-duplicates, and never places the case again. A group reduces no confirmation of receipt: confirmationsOwed() counts the reports. Ticks ai-features 2.2 (HermiqAiFeatureClientTest, absent hermiq over the real client) and 4.2; 4.1 backend built, the render on the case is open. Records the schema digests for fragments 43 and 44.
# Conflicts: # tests/schemas/schema-version-digests.json
…ion stays under the phpmd coupling limit
…iq, and is refused before sending without one src/services/caseAiFeatureGateway.js runs a case type's feature on a case document through hermiq's run-on-document endpoint (hermiq#1148), in the handler's own session, so hermiq reads the document as the handler. A request without a usable file id is refused before any request; hermiq's refusal comes back carrying its gate, and refusalSentence() names the feature and the reason. Ticks ai-features 3.1.
… absent-hermiq test declares the classes it uses
…ls and the report group ai-features-on-the-case-consume-hermiq 3.2 and 4.1 (board DqZaakAssistent): CaseAiFeaturesPanel as the case sidebar tab 'assistant'. Features show where they run as hermiq answers; a run on a case document goes through the gateway and a refusal names the feature, the reason and hermiq's gate. The report group shows its count and the near-duplicates. The case type's declaration is read first, so an undeclared case type asks nobody. Also drops a redundant psalm-suppress on RepairPortRegistrar (debt ratchet).
rubenvdlinde
requested review from
Rem-Dam,
SudoThijn,
WilcoLouwerse,
bbrands02,
remko48 and
rjzondervan
as code owners
October 10, 2026 20:16
This was referenced Oct 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #3629 (build/dq-l6-6), which stacks on #3625 and #3607. Land those first. Development is not merged in here: it conflicts only in files that stack touches (
appinfo/info.xml,.debt-baseline.json,catch-return-null.allowlist.json,schema-version-digests.json), and the CI shepherd resolves those on the base PRs.What this builds
ai-features-on-the-case-consume-hermiq, tasks 3.2, 4.1 and 8.1, plus the 8.2 e2e spec. Board DqZaakAssistent.
src/components/case/CaseAiFeaturesPanel.vue(new), wired as theassistantsidebar tab on CaseDetail (src/manifest.json,src/registry.js).caseAiFeatureGateway(feat(ai): a document-reading AI feature carries its reference to hermiq, and is refused before sending without one #3629) in the handler's own session.refusalSentenceandGate: <gate>. A transport failure gets its own separate line.src/services/caseAiFeaturesApi.js(new): the reads. It reads the case type'saiFeaturesdeclaration from OpenRegister first, so a case type that declares nothing makes no request about features. A failed read returns null, not an empty list.lib/AppInfo/Registrar/RepairPortRegistrar.php: removed@psalm-suppress UnusedClass, becauseServiceRegistrarinstantiates the class. That suppression (from 8609a83 on feat: build open OpenSpec changes (dossiq L6, part 3): agent write tools with reach #3583) is what turnedcheck:debt-ratchetred on feat(ai): a document-reading AI feature carries its reference to hermiq, and is refused before sending without one #3629 (psalm.inlineSuppressions 287 > 286).tests/vitest/caseTabConsolidation.spec.js: addedassistantto the exact sidebar tab list, with the reason.New code and its tests (coverage guard):
CaseAiFeaturesPanel.vue->tests/vitest/caseAiFeaturesPanel.spec.js(8 tests).caseAiFeaturesApi.js->tests/vitest/caseAiFeaturesApi.spec.js(11 tests). No new PHP class.Verified (exit codes)
tsc -p tests/e2e: 0. l10n check, check:l10n-js, check:schema-l10n: 0. debt-ratchet: 0.Still owed
npx playwright test tests/e2e/ai-features-on-the-case.spec.ts. hermiq's answers are routed in the browser, so no hermiq instance is needed.