diff --git a/frontend/src/__tests__/shell/routePermissions.test.ts b/frontend/src/__tests__/shell/routePermissions.test.ts
new file mode 100644
index 0000000..373528b
--- /dev/null
+++ b/frontend/src/__tests__/shell/routePermissions.test.ts
@@ -0,0 +1,56 @@
+import { describe, expect, it } from 'vitest';
+import { readFileSync } from 'node:fs';
+import { join } from 'node:path';
+
+/**
+ * Welle 3 (Kritikpunkt 7/8): Statische Route-Permissions müssen die
+ * Backend-/Manifest-Wahrheit spiegeln. Beweist per Source-Inspektion:
+ * - /communication → comm:read (Backend verlangt comm:read; communication:read
+ * war eine Phantom-Permission, die nirgends registriert war)
+ * - /mail/settings → mail:config (Backend mail-settings verlangt mail:config)
+ * - /import-export → import_export:read (Backend verlangt import_export:read;
+ * vorher fälschlich contacts:read)
+ * - /activity → audit:read (Seite nutzt Audit-API; activity:read war Phantom)
+ * - /wiki → wiki:read (Backend verlangt wiki:read; vorher ungeschützt)
+ */
+const routerSource = readFileSync(
+ join(__dirname, '..', '..', 'routes', 'index.tsx'),
+ 'utf-8',
+);
+
+function routePermission(path: string): string | null {
+ const marker = `path: '${path}'`;
+ const idx = routerSource.indexOf(marker);
+ if (idx === -1) return null;
+ const lineEnd = routerSource.indexOf('\n', idx);
+ const line = routerSource.slice(idx, lineEnd);
+ const match = line.match(/permission="([^"]+)"/);
+ return match ? match[1] : null;
+}
+
+describe('static route permissions match backend/manifest truth', () => {
+ it('communication uses comm:read (registered permission)', () => {
+ expect(routePermission('/communication')).toBe('comm:read');
+ });
+
+ it('mail/settings uses mail:config (backend requires mail:config)', () => {
+ expect(routePermission('/mail/settings')).toBe('mail:config');
+ });
+
+ it('import-export uses import_export:read (backend requires it)', () => {
+ expect(routePermission('/import-export')).toBe('import_export:read');
+ });
+
+ it('activity uses audit:read (page calls audit API)', () => {
+ expect(routePermission('/activity')).toBe('audit:read');
+ });
+
+ it('wiki is protected with wiki:read (backend requires wiki:read)', () => {
+ expect(routePermission('/wiki')).toBe('wiki:read');
+ });
+
+ it('phantom permissions are gone from the router', () => {
+ expect(routerSource).not.toContain('communication:read');
+ expect(routerSource).not.toContain('activity:read');
+ });
+});
diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx
index ae7b13d..30593bf 100644
--- a/frontend/src/routes/index.tsx
+++ b/frontend/src/routes/index.tsx
@@ -252,17 +252,17 @@ const router = createBrowserRouter([
{ path: '/dms/trash', element: {withSuspense()} },
{ path: '/trash', element: {withSuspense()} },
{ path: '/mail', element: {withSuspense()} },
- { path: '/mail/settings', element: {withSuspense()} },
+ { path: '/mail/settings', element: {withSuspense()} },
{ path: '/reports', element: {withSuspense()} },
{ path: '/tasks', element: {withSuspense()} },
- { path: '/communication', element: {withSuspense()} },
+ { path: '/communication', element: {withSuspense()} },
{ path: '/workflows', element: {withSuspense()} },
{ path: '/contacts/dedup', element: {withSuspense()} },
- { path: '/import-export', element: {withSuspense()} },
+ { path: '/import-export', element: {withSuspense()} },
{ path: 'tags', element: {withSuspense()} },
{ path: '/api-docs', element: {withSuspense()} },
- { path: '/activity', element: {withSuspense()} },
- { path: '/wiki', element: withSuspense() },
+ { path: '/activity', element: {withSuspense()} },
+ { path: '/wiki', element: {withSuspense()} },
{ path: '/system-dashboard', element: withSuspense() },
{ path: '/profile', element: withSuspense() },
{ path: '*', element: {} },