` (no padding) [VERIFIED: live codebase read]
+- `AdminAiConfigTab.vue` — top element: `
` (no padding at template level) [VERIFIED: live codebase read]
+- `AuditLogTab.vue` — top element: `
` (no padding) [VERIFIED: live codebase read]
+
+None of the tab components have top-level `p-8` or `max-w` classes. The `p-8 max-w-5xl mx-auto` is only in `AdminView.vue`. This means the extracted views do NOT need padding stripped — they are already padding-free. The padding moves from `AdminView.vue` to `AdminLayout.vue`'s content wrapper.
+
+Double-padding risk (PITFALLS.md §Pitfall 9) is not present in this specific codebase. [VERIFIED: all four tab files read directly]
+
+### Pattern 5: AdminSidebar structure (referenced from AppSidebar.vue)
+
+`AppSidebar.vue` uses these exact CSS patterns: [VERIFIED: live codebase read]
+
+- Sidebar container: `aside.w-64.bg-white.border-r.border-gray-200.flex.flex-col.h-full.shrink-0`
+- Logo header: `div.px-6.py-5.border-b.border-gray-100` with `h1.text-lg.font-bold.text-indigo-600.tracking-tight` + `p.text-xs.text-gray-400.mt-0.5` for subtitle
+- Nav section: `nav.flex-1.px-3.py-4.overflow-y-auto`
+- Nav link CSS classes (defined in `
+```
+
+### Router /admin nested route (complete structure)
+
+```javascript
+// Source: CONTEXT.md + PITFALLS.md §Pitfall 4 [VERIFIED: current router/index.js read]
+// Replace the existing flat '/admin' route:
+// { path: '/admin', component: () => import('../views/AdminView.vue'), meta: { requiresAdmin: true } }
+// With:
+{
+ path: '/admin',
+ component: () => import('../layouts/AdminLayout.vue'),
+ meta: { requiresAdmin: true },
+ children: [
+ { path: '', component: () => import('../views/admin/AdminOverviewView.vue') },
+ { path: 'users', component: () => import('../views/admin/AdminUsersView.vue') },
+ { path: 'quotas', component: () => import('../views/admin/AdminQuotasView.vue') },
+ { path: 'ai', component: () => import('../views/admin/AdminAiView.vue') },
+ { path: 'audit', component: () => import('../views/admin/AdminAuditView.vue') },
+ ],
+},
+```
+
+All admin views use lazy `() => import(...)` — consistent with existing auth views pattern and PERF-03 requirement in Phase 11.
+
+### Tailwind safelist (corrected from D-14)
+
+```javascript
+// frontend/tailwind.config.js — add safelist property
+// Source: formatters.js + AuditLogTab.vue direct reads [VERIFIED]
+import forms from '@tailwindcss/forms'
+export default {
+ content: ['./index.html', './src/**/*.{vue,js}'],
+ safelist: [
+ { pattern: /bg-(blue|sky|green|purple|orange|amber|gray|indigo|red)-(50|100|500|600)/ },
+ { pattern: /text-(blue|sky|green|purple|orange|amber|gray|indigo|red)-(400|500|600|700)/ },
+ ],
+ theme: { extend: {} },
+ plugins: [forms],
+}
+```
+
+### backend/api/admin/__init__.py update
+
+```python
+# Source: backend/api/admin/__init__.py [VERIFIED: live codebase read]
+from api.admin.overview import router as overview_router
+# Add alongside existing imports, then:
+router.include_router(overview_router)
+```
+
+---
+
+## State of the Art
+
+| Old Approach | Current Approach | When Changed | Impact |
+|--------------|------------------|--------------|--------|
+| Tab-based admin panel on user layout | Nested route subtree with dedicated layout | Phase 9 | Deep-linkable URLs, browser back button works |
+| `to.meta.requiresAdmin` guard | `to.matched.some(r => r.meta.requiresAdmin)` | Phase 9 | Security: guard now covers all child routes |
+| Flat `/admin` route | `/admin` parent + child routes under `AdminLayout` | Phase 9 | Standard Vue Router 4 layout nesting pattern |
+| All admins land at `/` then click Admin link | Admin users redirected to `/admin` after login | Phase 9 | Correct UX for operator-only accounts |
+
+---
+
+## Assumptions Log
+
+| # | Claim | Section | Risk if Wrong |
+|---|-------|---------|---------------|
+| A1 | SVG icon path strings for admin sidebar nav items (Overview=home, Users=group, Quotas=chart, AI=computer, Audit=clipboard) | Architecture Patterns §Pattern 5 | Wrong icon shown — cosmetic only, easy to fix |
+| A2 | Importing `_build_filtered_query_with_handles` from `api.audit` inside `overview.py` does not create a circular import | Architecture Patterns §Pattern 6 | ImportError at startup if circular; verify by checking `api.audit` imports |
+
+**All other claims in this research were verified by direct codebase read.**
+
+---
+
+## Open Questions
+
+1. **AdminSidebar — separate component or inline in AdminLayout?**
+ - What we know: `AppSidebar.vue` is a separate component imported by `App.vue`. `AuthLayout.vue` has no sidebar.
+ - Recommendation: Create `AdminSidebar.vue` as a separate component in `components/admin/` for consistency with the existing architecture pattern. `AdminLayout.vue` imports it.
+
+2. **`AdminOverviewView.vue` — does it need a Pinia store?**
+ - What we know: All other admin tab components (`AdminUsersTab`, etc.) fetch data directly with `api.*` calls in `onMounted`. D-03 says overview is a single-fetch endpoint. No coordination logic needed.
+ - Recommendation: No store. Fetch directly in `onMounted` with local `ref` state, consistent with all other admin tab components.
+
+3. **Comment purge scope for Phase 8 backend sub-packages — which files?**
+ - What we know: `api/admin/users.py`, `api/admin/quotas.py`, `api/admin/ai.py`, `api/documents/upload.py`, `api/documents/crud.py`, `api/documents/content.py`, `api/auth/tokens.py`, `api/auth/totp.py`, `api/auth/password.py`, `api/auth/sessions.py` are all Phase 8 products.
+ - Recommendation: The comment purge plan should enumerate all Phase 8 backend sub-package files explicitly so nothing is missed.
+
+---
+
+## Environment Availability
+
+Step 2.6: SKIPPED — Phase 9 is purely frontend restructuring + one new backend endpoint. No new external tools, services, or CLIs are required. All dependencies are already installed and running.
+
+---
+
+## Validation Architecture
+
+### Test Framework
+
+| Property | Value |
+|----------|-------|
+| Framework | pytest (backend) |
+| Config file | `backend/pytest.ini` or `backend/pyproject.toml` |
+| Quick run command | `cd backend && pytest tests/test_admin_overview.py -x` |
+| Full suite command | `cd backend && pytest -v` |
+
+### Phase Requirements → Test Map
+
+| Req ID | Behavior | Test Type | Automated Command | File Exists? |
+|--------|----------|-----------|-------------------|-------------|
+| ADMIN-12 | `to.matched.some()` guard — non-admin cannot access `/admin/users` directly | Integration (frontend router) | Manual browser test or Playwright | No — Wave 0 gap |
+| ADMIN-11 | `GET /api/admin/overview` returns correct aggregate stats | Integration (backend) | `pytest tests/test_admin_overview.py -x` | No — Wave 0 gap |
+| ADMIN-11 | Overview response never includes `credentials_enc` or document content | Security test | `pytest tests/test_admin_overview.py::test_overview_no_sensitive_fields` | No — Wave 0 gap |
+| ADMIN-08 | `AdminView.vue` is deleted — no file imports it | Static verification | `grep -r "AdminView" frontend/src/` returns nothing | N/A — verified by grep |
+| CODE-06 | Dynamic color classes render in production build | Build test | `npm run build` then visual check | N/A — manual |
+
+### Sampling Rate
+
+- **Per task commit:** `cd backend && pytest tests/test_admin_overview.py -x` (after overview endpoint)
+- **Per wave merge:** `cd backend && pytest -v`
+- **Phase gate:** Full suite green before `/gsd:verify-work`
+
+### Wave 0 Gaps
+
+- `backend/tests/test_admin_overview.py` — covers ADMIN-11 aggregate query + security invariants
+- Consider: `frontend/src/views/__tests__/AdminLayout.spec.js` if frontend test infra supports it (check `frontend/src/views/__tests__/` existence)
+
+---
+
+## Security Domain
+
+### Applicable ASVS Categories
+
+| ASVS Category | Applies | Standard Control |
+|---------------|---------|-----------------|
+| V2 Authentication | Partial | Guard + role check via `authStore.user.role` |
+| V3 Session Management | No | Existing session management unchanged |
+| V4 Access Control | Yes | `requiresAdmin` guard on all `/admin/*` children via `to.matched.some()` |
+| V5 Input Validation | No | `GET /api/admin/overview` has no user-supplied input |
+| V6 Cryptography | No | No crypto operations in this phase |
+
+### Known Threat Patterns
+
+| Pattern | STRIDE | Standard Mitigation |
+|---------|--------|---------------------|
+| Non-admin accessing `/admin/users` directly via URL | Elevation of Privilege | `to.matched.some(r => r.meta.requiresAdmin)` + `get_current_admin` dep on backend |
+| Admin redirect bypass via `?redirect=/` query param | Privilege Escalation | D-08 login redirect checks `user.role` first; `route.query.redirect` honored only for same-role routes |
+| `GET /api/admin/overview` returning sensitive data | Information Disclosure | Whitelist response fields; never return `password_hash`, `credentials_enc`, `extracted_text`; use same `_audit_to_dict_with_handles` whitelist for audit entries |
+
+**Admin endpoint invariant (from CLAUDE.md):** `GET /api/admin/overview` must never include document content, extracted text, or `credentials_enc`. The overview response contains only aggregate counts and audit log summary rows — no document-level data. [VERIFIED by response design in Pattern 6]
+
+---
+
+## Sources
+
+### Primary (HIGH confidence)
+
+- `frontend/src/router/index.js` — current guard structure, existing `/admin` route definition, all route meta patterns
+- `frontend/src/App.vue` — current layout switch pattern (2 branches: auth/main)
+- `frontend/src/layouts/AuthLayout.vue` — reference pattern for standalone layout component
+- `frontend/src/components/layout/AppSidebar.vue` — CSS classes, SVG icon family, nav-link/nav-link-active, user footer structure
+- `frontend/src/views/AdminView.vue` — current tab structure being replaced
+- `frontend/src/components/admin/AdminUsersTab.vue` — no top-level padding confirmed
+- `frontend/src/components/admin/AdminQuotasTab.vue` — no top-level padding confirmed
+- `frontend/src/components/admin/AuditLogTab.vue` — `actionTypeClass()` amber/purple/blue color usage
+- `frontend/src/utils/formatters.js` — exact dynamic classes: `text-sky-500`, `bg-sky-50`, `text-orange-500`, `text-gray-400`, `bg-orange-50`
+- `frontend/tailwind.config.js` — current config (no safelist, forms plugin already wired)
+- `frontend/src/stores/auth.js` — `user.value = data.user` set on login, `user.role` field exists
+- `frontend/src/views/auth/LoginView.vue` — `handleLoginResult` redirect logic
+- `backend/api/admin/__init__.py` — aggregator pattern, `router = APIRouter(prefix="/api/admin")`
+- `backend/api/admin/users.py` — `router = APIRouter()` with NO prefix confirmed
+- `backend/api/admin/shared.py` — `_user_to_dict` whitelist pattern
+- `backend/api/audit.py` — `_build_filtered_query_with_handles`, `_audit_to_dict_with_handles` — importable query helpers
+- `backend/db/models.py` — `User`, `Quota`, `Document` ORM models for aggregate query
+- `.planning/research/PITFALLS.md` — Pitfall 3 (meta guard), Pitfall 4 (App.vue double render), Pitfall 9 (double padding), Pitfall 14 (Tailwind safelist)
+- `.planning/codebase/ARCHITECTURE.md` — component responsibility map, layering
+- `.planning/phases/09-admin-panel-rearchitecture/09-CONTEXT.md` — all locked decisions D-01 through D-16
+
+### Secondary (MEDIUM confidence)
+
+- CONTEXT.md §Established Patterns — confirms `api/admin/__init__.py` "sub-routers carry NO prefix" invariant
+
+### Tertiary (LOW confidence)
+
+- SVG icon path strings for admin sidebar (A1 in Assumptions Log) — consistent with Heroicons outline family used in codebase but exact paths not individually verified against heroicons.com
+
+---
+
+## Metadata
+
+**Confidence breakdown:**
+
+- Standard stack: HIGH — Phase 9 installs nothing new; all existing packages verified by direct file reads
+- Architecture: HIGH — all patterns derived from live codebase reads + PITFALLS.md
+- Pitfalls: HIGH — grounded in actual source files as annotated in PITFALLS.md header
+- Tailwind safelist: HIGH — `formatters.js` and `AuditLogTab.vue` read directly; color families catalogued precisely
+
+**Research date:** 2026-06-12
+**Valid until:** 2026-07-12 (stable codebase — no external dependencies change)