` (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 (RESOLVED)
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)