diff --git a/.claude/openspec/architecture/adr-001-data-layer.md b/.claude/openspec/architecture/adr-001-data-layer.md deleted file mode 100644 index 6b52c806..00000000 --- a/.claude/openspec/architecture/adr-001-data-layer.md +++ /dev/null @@ -1,221 +0,0 @@ -- ALL domain data → OpenRegister objects. NO custom Entity/Mapper for domain data. -- App config → `IAppConfig`. NOT OpenRegister. -- Cross-entity references: OpenRegister relations (register+schema+objectId). NO foreign keys. - MUST NOT store foreign keys or embed full objects. - -### Schema standards - -- Schemas: PascalCase, schema.org vocabulary, explicit types + required flags + description field. -- MUST NOT invent custom property names when a schema.org equivalent exists. -- Contact schemas MUST align with vCard properties (fn, email, tel, adr). -- Dutch government fields SHOULD use a mapping layer translating between international standards - and Dutch specs — do not hardcode Dutch field names as primary. -- Schema changes that remove or rename properties are BREAKING. Adding optional properties is non-breaking. - -### Register templates - -- Location: `lib/Settings/{app}_register.json` (OpenAPI 3.0 + `x-openregister` extensions). -- Three template categories: - - **App configuration** — define data models (schemas/registers/views/mappings). - Mark with `x-openregister.type: "application"`. - - **Mock data** — fictional but realistic seed data for dev/test. - Mark with `x-openregister.type: "mock"`. - - **Government standards** — aligned to Dutch API specs (BAG, BRP, KVK, DSO). -- Import mechanism: `ConfigurationService::importFromApp(appId, data, version, force)` → - `ImportHandler::importFromApp()`. Called from repair step or `SettingsLoadService`. -- Idempotency: re-importing with `force: false` MUST NOT create duplicates. Match by slug - using `ObjectService::searchObjects` with `_rbac: false` and `_multitenancy: false`. - Use `version_compare` for skip logic. - -### Seed data - -Apps that store data in OpenRegister are empty on first install. An empty app cannot be -meaningfully tested — there are no objects to view, search, filter, or interact with. -This blocks both automated browser testing and manual QA. The Loadable Register Template -pattern (see Register templates above) already supports seed data via `components.objects[]` -with the `@self` envelope. - -**Requirements:** - -- Every app using OpenRegister MUST include 3-5 realistic objects per schema in - `lib/Settings/{app}_register.json`. -- Use `@self` envelope: `{ "@self": { "register": ..., "schema": ..., "slug": ... }, ...properties }`. - Register/schema MUST match keys; slug is unique human-readable identifier for matching. -- Use general organisation data (municipality, consultancy, travel agency, non-profit) — - NOT context-specific. Varied, realistic field values. -- Mock data quality: real Dutch street names, valid postcodes (`[1-9][0-9]{3}[A-Z]{2}`), - correct municipality/KVK codes, BSNs that pass 11-proef. Fictional but distinguishable from real. -- Cross-register consistency: BRP→BAG, KVK→BAG, DSO→BAG references must be valid. -- Loaded on install alongside schemas via same `importFromApp()` pipeline. -- MUST be idempotent — re-importing skips existing objects matched by slug. - -**In OpenSpec artifacts:** - -- **In design.md**: MUST include a Seed Data section when change introduces/modifies schemas — - define seed objects per schema with concrete field values and related items (files, notes, tasks, contacts). -- **In tasks.md**: MUST include a seed data generation task when change introduces/modifies schemas. - -**Exceptions** (no seed data required): - -- **nldesign** — has no OpenRegister schemas. -- **ExApp sidecar wrappers** (openklant, opentalk, openzaak, valtimo, n8n-nextcloud) — proxy - external services and do not use OpenRegister. -- **nextcloud-vue** — shared library, no seed data applicable. -- Changes that only modify frontend components or non-schema backend logic (e.g., settings, - permissions) do not require seed data. - -**Limitations:** OpenRegister's `ImportHandler` currently supports only flat seed objects. -Related items (files, notes, tasks, contacts) linked through the relation system are tracked -on the product roadmap. Until then, seed data is limited to object properties defined in schemas. - -### Deduplication check - -- Before proposing new capability: search `openspec/specs/` and `openregister/lib/Service/` for overlap - with ObjectService, RegisterService, SchemaService, ConfigurationService, and shared Vue components. -- If similar capability exists: MUST reference it and explain why new code is needed rather than extending. -- Proposals duplicating existing functionality without justification MUST be rejected. -- **In design.md**: MUST include a "Reuse Analysis" section listing existing OpenRegister services leveraged. -- **In tasks.md**: MUST include a "Deduplication Check" task verifying no overlap — document findings - even if "no overlap found". - -### Schema migrations - -- Breaking schema changes → new migration in repair step. NEVER modify existing migrations. - -### OpenRegister + @conduction/nextcloud-vue — DO NOT REBUILD - -The platform provides 258+ backend methods and 69+ frontend components. Apps ONLY build -custom logic for domain-specific business rules. Everything below is provided for FREE. - -**CRUD & Data Management** (use ObjectService + CnIndexPage + CnDetailPage): -- Single & bulk create, read, update, delete — `ObjectService.saveObject()`, `deleteObject()` -- List with pagination, sorting, filtering — `ObjectService.findAll()` + `CnDataTable` -- Schema-driven forms — `CnFormDialog` (auto-generates from schema) or `CnAdvancedFormDialog` -- Detail views — `CnDetailPage` with `CnDetailGrid`, `CnDetailCard` sections -- Record merging/deduplication — `ObjectService.mergeObjects()` -- Object locking — `ObjectService.lockObject()` / `unlockObject()` - -**Import & Export** (use ImportService/ExportService + CnMassImportDialog/CnMassExportDialog): -- CSV, Excel, JSON import with intelligent field mapping — `ImportService` -- CSV, Excel, JSON export with column selection — `ExportService` -- Bulk import with validation and progress — `CnMassImportDialog` -- Filtered export with format picker — `CnMassExportDialog` -- NO custom import dialogs, parsers, upload handlers, or export controllers - -**Search & Discovery** (use IndexService + CnFilterBar + CnFacetSidebar): -- Full-text search with field weighting — `IndexService` -- Faceted navigation with counts — `FacetBuilder` + `CnFacetSidebar` -- Semantic search with embeddings — `VectorizationService` -- Hybrid search (keyword + semantic) — automatic -- Search analytics — `SearchTrailService` (popular terms, activity) -- NO custom search endpoints, query builders, or search pages - -**File Management** (use FileService + CnObjectSidebar): -- Upload (single/multipart), download, share links — `FileService` -- File tagging, public/private toggle — `FileService` -- Bulk download as ZIP — `createObjectFilesZip()` -- Text extraction from PDFs/Office docs — `TextExtractionService` -- File tab in object sidebar — `CnObjectSidebar` → `CnFilesTab` -- NO custom file upload components, file controllers, or download handlers - -**Audit & Compliance** (use AuditTrailService + CnObjectSidebar): -- Full change tracking with before/after snapshots — automatic -- Audit trail tab — `CnObjectSidebar` → `CnAuditTrailTab` -- GDPR data subject access requests — `inzageverzoek()`, `verwerkingsregister()` -- Audit export and analytics — `AuditTrailController` -- NO custom audit logging, change tracking, or compliance controllers - -**Dashboard & Analytics** (use CnDashboardPage + CnChartWidget + CnStatsBlock): -- Drag-drop widget dashboard — `CnDashboardPage` with GridStack -- KPI cards — `CnKpiGrid`, `CnStatsBlock`, `CnStatsPanel` -- Charts (line/bar/pie/donut) — `CnChartWidget` (ApexCharts) -- Data tables as widgets — `CnTableWidget` -- Editable data grids — `CnObjectDataWidget` -- NO custom dashboard layouts, chart components, or KPI cards - -**Forms & Dialogs** (use CnFormDialog + schema-driven generation): -- Auto-generated create/edit forms — `CnFormDialog` reads schema → generates fields -- JSON/metadata editing — `CnAdvancedFormDialog` with Properties/Data/Metadata tabs -- Schema editor — `CnSchemaFormDialog` -- Delete/Copy/Mass operations — `CnDeleteDialog`, `CnCopyDialog`, `CnMassDeleteDialog` -- NO custom form components, validation logic, or dialog wrappers - -**Navigation & Pagination** (use CnPagination + CnActionsBar + useListView): -- Pagination control with size selector — `CnPagination` -- Action bar (add, search, toggle views) — `CnActionsBar` -- List state management — `useListView` composable (handles search, filter, sort, page) -- Detail state management — `useDetailView` composable -- NO custom pagination logic, debounced search, or list state management - -**Authorization & RBAC** (use AuthorizationService + PropertyRbacHandler): -- Role-based access control — `AuthorizationService` -- Field-level permissions — `PropertyRbacHandler` -- Object-level restrictions — `PermissionHandler` -- Authorization audit — `AuthorizationAuditService` -- NO custom permission checks, role systems, or access control middleware - -**Webhooks & Events** (use WebhookService): -- Create, test, retry webhooks — `WebhookService` -- CloudEvents format — automatic -- Event subscriptions — selective per schema/action -- NO custom webhook controllers or event dispatchers - -**Notifications & Activity** (use NotificationService + ActivityService): -- Nextcloud notifications — `NotificationService` -- Activity feed — `ActivityService` -- Calendar events — `CalendarEventService` -- Deck/Kanban cards — `DeckCardService` - -**Store & State** (use createObjectStore + plugins): -- Object stores — `createObjectStore(name)` generates Pinia CRUD store -- Store plugins: `auditTrails`, `files`, `lifecycle`, `relations`, `search`, `selection` -- Column/field/filter generation from schema — `columnsFromSchema()`, `fieldsFromSchema()` -- NO custom Pinia stores for CRUD, Vuex, or manual API call management - -**Chat & AI** (use ChatService): -- Multi-turn conversation — `ChatService` -- RAG-based knowledge retrieval — `ContextRetrievalHandler` -- LLM response generation — `ResponseGenerationHandler` - -**Data Retention & Archival** (use ArchivalService): -- Legal hold — `LegalHoldService` -- Destruction schedules — `DestructionService` -- Retention policies — `RetentionService` - -**Semantic & Hybrid Search** (use SolrController + SettingsController): -- Semantic search via vector embeddings — `SettingsController.semanticSearch()` -- Hybrid search (keyword + semantic combined) — `SolrController.hybridSearch()` -- Vector embedding generation — `VectorizationService` -- NO custom search algorithms — configure via OpenRegister settings - -**GraphQL API** (use GraphQLController): -- Query objects across schemas via GraphQL — `GraphQLController.execute()` -- Alternative to REST for complex cross-entity queries - -**Organization / Multi-Tenancy** (use OrganisationController): -- Organization CRUD — `OrganisationController` -- Tenant-scoped data isolation — automatic via `TenantLifecycleService` -- NO custom multi-tenancy logic - -**Task & Workflow Management** (use TasksController + WorkflowEngineController): -- Task creation and tracking — `TasksController` -- Workflow orchestration — `WorkflowEngineRegistry` -- Scheduled workflows — `ScheduledWorkflowController` -- NO custom task/workflow systems - -**Text Extraction** (use FileTextController): -- Extract text from PDFs and Office docs — `TextExtractionService` -- Entity recognition (PII detection) — `EntityRecognitionHandler` -- Content anonymization — automatic - -**Timeline & Stages** (use CnTimelineStages): -- Workflow progression visualization — `CnTimelineStages` component -- Stage tracking with status colors - -### What apps SHOULD build (custom business logic only): -- External API integrations (SAP, Peppol, TenderNed, etc.) -- PDF/document generation with business-specific templates -- Workflow triggers and business rules specific to the domain -- Notification dispatch with app-specific event types -- Custom settings pages with app-specific configuration -- Background jobs for domain-specific processing diff --git a/.claude/openspec/architecture/adr-002-api.md b/.claude/openspec/architecture/adr-002-api.md deleted file mode 100644 index 4f956593..00000000 --- a/.claude/openspec/architecture/adr-002-api.md +++ /dev/null @@ -1,6 +0,0 @@ -- URL pattern: `/index.php/apps/{app}/api/{resource}` — lowercase plural, hyphens. -- Methods: GET=read, POST=create, PUT=update, DELETE=remove. No custom methods. -- Pagination: support `_page` + `_limit`. Response includes `total`, `page`, `pages`. -- Errors: appropriate HTTP status + `message` field. NO stack traces in responses. -- Auth: Nextcloud built-in only. NO custom login/session/token flows. -- Public endpoints: annotate `#[PublicPage]` + `#[NoCSRFRequired]`. Register CORS OPTIONS route. diff --git a/.claude/openspec/architecture/adr-003-backend.md b/.claude/openspec/architecture/adr-003-backend.md deleted file mode 100644 index 82abe764..00000000 --- a/.claude/openspec/architecture/adr-003-backend.md +++ /dev/null @@ -1,14 +0,0 @@ -- **Controller → Service → Mapper** (strict 3-layer). Controllers NEVER call mappers directly. -- Controllers: thin (<10 lines/method). Routing + validation + response only. -- Services: ALL business logic. Stateless — no instance state between requests. -- Mappers: DB CRUD only. No business logic. -- DI: constructor injection with `private readonly`. NO `\OC::$server` or static locators. -- Entity setters: POSITIONAL args only. `$e->setName('val')` — NEVER `$e->setName(name: 'val')`. - (`__call` passes `['name' => val]` but `setter()` uses `$args[0]`.) -- Routes: `appinfo/routes.php`. Specific routes BEFORE wildcard `{slug}` routes. -- Config: `IAppConfig` with sensitive flag for secrets. NEVER read DB directly. -- Lifecycle: schema init via repair steps (`IRepairStep`), background via job queue, events via dispatcher. -- **Spec traceability**: every class and public method MUST have `@spec` PHPDoc tag(s) linking to - the OpenSpec change that caused it: `@spec openspec/changes/{name}/tasks.md#task-N`. - Multiple `@spec` tags allowed (code touched by multiple changes). File-level `@spec` in header docblock. - This enables: code → docblock → spec traceability alongside code → git blame → commit → issue → spec. diff --git a/.claude/openspec/architecture/adr-004-frontend.md b/.claude/openspec/architecture/adr-004-frontend.md deleted file mode 100644 index 2484aa21..00000000 --- a/.claude/openspec/architecture/adr-004-frontend.md +++ /dev/null @@ -1,129 +0,0 @@ -- **Vue 2 + Pinia + @nextcloud/vue + @conduction/nextcloud-vue**. NO Vuex. Options API only. -- State: Pinia stores in `src/store/modules/`. Use `createObjectStore` for OpenRegister CRUD. -- API calls: `axios` from `@nextcloud/axios` — auto-attaches CSRF token. NEVER raw `fetch()` for mutations. - Loading state with `try/finally`. -- Translations: ALL user-visible strings via `t(appName, 'text')`. NO hardcoded strings. - Translation keys MUST be English — Dutch translations go in `l10n/nl.json`. -- CSS: ONLY Nextcloud CSS variables (`var(--color-primary-element)`, etc.). NO hardcoded colors. - NEVER reference `--nldesign-*` directly — nldesign app handles theming. -- Router: history mode, base `generateUrl('/apps/{app}/')`. Requires matching PHP routes in `routes.php`. - Deep link URL templates MUST match the router mode — use path format (`/apps/{app}/entities/{uuid}`), - NOT hash format (`/apps/{app}/#/entities/{uuid}`). -- OpenRegister dependency: settings returns `openRegisters` (bool) + `isAdmin`. - Show empty state if OR missing. NEVER use `OC.isAdmin` — get from backend. -- NEVER `window.confirm()` or `window.alert()` — use `NcDialog` or `CnFormDialog` (WCAG, theming). -- NEVER read app state from DOM (`document.getElementById`, `dataset`) — use backend API or store. -- EVERY `await store.action()` call MUST be wrapped in `try/catch` with user-facing error feedback. -- NEVER import from `@nextcloud/vue` directly — use `@conduction/nextcloud-vue` which re-exports all - NC components plus Conduction components. This ensures consistent theming and component versions. -- EVERY component used in `