Skip to main content

ADR compliance audit — MyDash

Audit of the 23 org-wide ADRs (hydra/openspec/architecture/adr-*.md) against what MyDash actually does. Audit date: 2026-04-24, after consolidating 6 in-flight feature PRs into development and landing the template + ADR cleanup PR.

Legend: ✅ compliant · ⚠️ partial · ❌ gap · N/A out of scope

Matrix

ADRRule (short)StatusNote
001 Data layerApp config → IAppConfig, not OpenRegisterN/AMyDash owns its own Doctrine entities (Dashboard, Tile, WidgetPlacement, ConditionalRule, AdminSetting). Self-contained domain model, no OpenRegister dependency
001Register JSON at lib/Settings/{app}_register.jsonN/Ano domain schemas exposed to OR
002 APIURL pattern /api/{resource}, standard verbsall 17 routes in appinfo/routes.php follow REST conventions
002No stack traces in error responsesResponseHelper::error now returns generic message (fixed in this PR)
002PaginationN/Adashboards / tiles lists are small per-user; no pagination needed
003 BackendController → Service → Mapper layering11 controllers delegate to 20 services which delegate to 20 mappers/entities
003Thin controllersmost methods under 20 lines; DashboardApiController::update is the largest at ~45 lines due to metadata-vs-layout branch
003DI via constructor + private readonlyverified across lib/Controller/ and lib/Service/
003No \OC::$server or static locatorspost-PR: `grep -rnE '\OC::\$server\
003@spec on every class + public method62 @spec tags now annotate every public method that maps to a capability Requirement (Bucket 1 from openspec/coverage-report.md); enforced by composer lint:spec-annotations against tools/spec-annotations-allowlist.txt
003Specific routes before wildcardno wildcard routes
004 FrontendVue 2 + Pinia + Options APIVue 2.7 + Pinia stores + Webpack (template-aligned)
004Never import from @nextcloud/vue directly — use @conduction/nextcloud-vuepost-PR: all 9 direct imports migrated
004All user-visible strings via t(appName, '…')verified across src/components/ and src/views/
004CSS uses NC variables only--color-* tokens used throughout
004Never window.confirm() / alert()uses NcDialog via @conduction/nextcloud-vue
005 SecurityAdmin check on backend, not frontendcontroller methods verify admin group membership
005#[NoAdminRequired] paired with per-object auth checkverified: DashboardApiController::update routes to PermissionService::canEditDashboard; DashboardService::deleteDashboard + activateDashboard check $dashboard->getUserId() !== $userId; TileService + WidgetService follow the same pattern at service layer
005#[PasswordConfirmationRequired] on admin mutations⚠️admin-console mutations could benefit; deferred for a focused security review pass
005No stack traces / exception messages in API responsesResponseHelper::error now returns generic 'Operation failed' (fixed in this PR)
006 Metrics/api/metrics + /api/healthMetricsController exposes Prometheus text format at /api/metrics; HealthController at /api/health
007 i18nEnglish primary + Dutch requiredl10n/en.json (5485 B) + l10n/nl.json (5798 B); JS mirrors present
007Frontend t(appName, 'key') + n(...) for pluralsspot-check shows translatePlural used for count-based strings
007Backend $this->l10n->t('key')admin settings strings use IL10N
008 TestingPHPUnit coverage per service / controller⚠️8 unit tests present (DashboardFactory, VisibilityChecker, Tile, ConditionalRule, AdminSetting, …). Service+controller coverage grows with each opsx change
008Newman/Postman collectiondeferred — no tests/integration/*.postman_collection.json. Tracked as openspec change + issue
009 DocsUser-facing features documentedDocusaurus site at docs/ with 10+ feature docs under docs/features/ + intro + dev guide. Architecture doc added in this PR
010 NL DesignCSS custom properties, no hardcoded colorsverified
010WCAG AA⚠️basic keyboard nav + focus handling present; no structured axe-core or NVDA pass run. Consistent with "admin-only app" guidance, but deeper audit worth a focused PR
011 Schema standardsschema.org vocabularyN/Ano domain schemas exposed externally
012 DedupReuse analysis in OpenSpec changeseach archived change includes a "reuse analysis" section (spot-check on 2026-03-21-dashboards/design.md)
013 Container poolHydra infra concernN/Anot an app concern
014 LicensingEUPL-1.2 on every source filepost-PR: 72 PHP files have clean @license EUPL-1.2 PHPDoc; contradictory SPDX-License-Identifier: AGPL-3.0-or-later block removed; REUSE.toml added for machine-readable REUSE compliance
014info.xml licence element<licence>agpl</licence> retained as the Nextcloud app-store schema element (schema expects agpl) — separate from source licence; documented in commit ab9fc70
015 Common patternsStatic generic error messagesResponseHelper::error returns generic text (fixed in this PR)
015No raw fetch() — use @nextcloud/axiosverified: grep -rn 'fetch(' src/ returns zero
015EUPL headerssee ADR-014
016 Routesappinfo/routes.php is the only registration path`grep -rE '#\[ApiRoute\
017 Component compositionAvoid wrapping self-contained componentsno file in src/ exceeds 537 lines (WidgetPicker.vue); components use the @conduction/nextcloud-vue dashboard primitives
018 Widget header actionsheader-actions slot on cards⚠️MyDash renders legacy Nextcloud dashboard widgets (NcDashboardWidget) rather than OR-backed CnDetailCard / CnObjectDataWidget. That's by design (MyDash is a container app, not an OR data consumer), but ADR-018 contemplates header-actions on data cards. Treated as N/A-by-design for now; revisit if MyDash grows OR-backed tile types
019 Integration registrySidebar tabs / linked itemsN/AMyDash is a widget consumer, not a registry provider
020 Gate scopeHydra gate scope is PR diffN/Areviewer guidance, not app code
021 Bounded fix scopeReviewer bounded-fix by change shapeN/Areviewer guidance, not app code
022 Apps consume OR abstractionsRBAC / audit / archival via ORN/Ano OR consumption — see ADR-001 note
023 Action authorizationAdmin-configured action/group mappingsN/AMyDash uses role-based permissions (view / add_only / full), not fine-grained action mapping

Summary

  • Compliant: 26 rules (incl. compound rules)
  • Partial: 4 rules (ADR-005 PasswordConfirmationRequired, ADR-008 PHPUnit breadth, ADR-010 deep WCAG AA, ADR-018 header-actions slot by design)
  • Gaps: 1 rule (ADR-008 Newman)
  • N/A: 14 rules (no OR consumption, no registry provider, no fine- grained actions, hydra-infra rules)

Follow-ups

The remaining item is tracked as a formal OpenSpec change + GitHub issue so Hydra's pipeline can pick it up after this PR merges:

  1. Newman / Postman integration collection — covering the 17 OCS endpoints, with env-placeholder credentials and CI wiring.

Partial items that may warrant their own follow-up PRs later (not filed as Hydra-triggered issues today):

  1. PasswordConfirmationRequired on admin-console mutations (ADR-005). The admin console writes template definitions + global settings; a stolen session shouldn't silently alter those. Focused security review.
  2. Deep WCAG AA audit (ADR-010) — axe-core + manual screen-reader traversal on the dashboard + tile editor surfaces.
  3. Thread LoggerInterface through the 6 controllers that currently don't inject one, so ResponseHelper::error($e, logger: $this->logger) logs the real exception server-side. The leak is already closed client-side; this restores visibility.