diff --git a/.planning/phases/09-admin-panel-rearchitecture/09-01-PLAN.md b/.planning/phases/09-admin-panel-rearchitecture/09-01-PLAN.md
index e9cc4d1..a7c848c 100644
--- a/.planning/phases/09-admin-panel-rearchitecture/09-01-PLAN.md
+++ b/.planning/phases/09-admin-panel-rearchitecture/09-01-PLAN.md
@@ -133,10 +133,10 @@ From backend/api/admin/__init__.py (current state):
- `user_count` is `SELECT count(id) FROM users WHERE role = 'user'`.
- `total_storage_bytes` is `SELECT sum(used_bytes) FROM quotas` coerced to `0` when NULL.
- `doc_status` is `{status: count}` from `SELECT status, count(id) FROM documents GROUP BY status`.
- - `recent_audit` is the result of `_build_filtered_query_with_handles(None, None, None, None).limit(10)` mapped via `_audit_to_dict_with_handles`.
+ - `recent_audit` is the result of `_build_filtered_query_with_handles(None, None, None, None).order_by(AuditLog.created_at.desc()).limit(10)` mapped via `_audit_to_dict_with_handles` — the `.order_by(AuditLog.created_at.desc())` must be chained before `.limit(10)` to guarantee newest-first ordering that the test asserts.
- Response never includes raw user records, raw audit rows, password_hash, credentials_enc, totp_secret, api_key_enc, or extracted_text.
- Create `backend/api/admin/overview.py`. Imports: `from __future__ import annotations`; `from fastapi import APIRouter, Depends`; `from sqlalchemy import func, select`; `from sqlalchemy.ext.asyncio import AsyncSession`; `from db.models import Document, Quota, User`; `from deps.auth import get_current_admin`; `from deps.db import get_db`; `from api.audit import _build_filtered_query_with_handles, _audit_to_dict_with_handles`. Declare `router = APIRouter()` with NO prefix (single WHY comment: "NO prefix — parent `__init__.py` carries `/api/admin` (D-02)"). Declare `@router.get("/overview")` async function `get_overview(session: AsyncSession = Depends(get_db), _admin: User = Depends(get_current_admin)) -> dict`. Compute user_count via `await session.scalar(select(func.count(User.id)).where(User.role == "user"))` (default to 0 if None). Compute total_storage_bytes via `await session.scalar(select(func.sum(Quota.used_bytes)))` (default 0). Compute doc_status via `(await session.execute(select(Document.status, func.count(Document.id)).group_by(Document.status))).all()` and convert to dict. Compute recent_audit by calling `_build_filtered_query_with_handles(None, None, None, None).limit(10)`, executing, then mapping each row tuple via `_audit_to_dict_with_handles(*row)` — confirm the row unpacking shape against the audit.py source (the existing list endpoint already does this — copy that exact iteration). Return the dict with the four keys. Modify `backend/api/admin/__init__.py`: add `from api.admin.overview import router as overview_router` alongside existing imports (preserve the docstring WHY comment); append `router.include_router(overview_router)` after the existing three `include_router` calls. Then remove the `@pytest.mark.xfail` decorators from all 8 tests in `backend/tests/test_admin_overview.py` so they execute as real tests.
+ Create `backend/api/admin/overview.py`. Imports: `from __future__ import annotations`; `from fastapi import APIRouter, Depends`; `from sqlalchemy import func, select`; `from sqlalchemy.ext.asyncio import AsyncSession`; `from db.models import Document, Quota, User`; `from deps.auth import get_current_admin`; `from deps.db import get_db`; `from api.audit import _build_filtered_query_with_handles, _audit_to_dict_with_handles`. Declare `router = APIRouter()` with NO prefix (single WHY comment: "NO prefix — parent `__init__.py` carries `/api/admin` (D-02)"). Declare `@router.get("/overview")` async function `get_overview(session: AsyncSession = Depends(get_db), _admin: User = Depends(get_current_admin)) -> dict`. Compute user_count via `await session.scalar(select(func.count(User.id)).where(User.role == "user"))` (default to 0 if None). Compute total_storage_bytes via `await session.scalar(select(func.sum(Quota.used_bytes)))` (default 0). Compute doc_status via `(await session.execute(select(Document.status, func.count(Document.id)).group_by(Document.status))).all()` and convert to dict. Compute recent_audit by calling `_build_filtered_query_with_handles(None, None, None, None).order_by(AuditLog.created_at.desc()).limit(10)`, executing, then mapping each row tuple via `_audit_to_dict_with_handles(*row)` — confirm the row unpacking shape against the audit.py source (the existing list endpoint already does this — copy that exact iteration). Return the dict with the four keys. Modify `backend/api/admin/__init__.py`: add `from api.admin.overview import router as overview_router` alongside existing imports (preserve the docstring WHY comment); append `router.include_router(overview_router)` after the existing three `include_router` calls. Then remove the `@pytest.mark.xfail` decorators from all 8 tests in `backend/tests/test_admin_overview.py` so they execute as real tests.
cd backend && pytest tests/test_admin_overview.py -v -x
diff --git a/.planning/phases/09-admin-panel-rearchitecture/09-03-PLAN.md b/.planning/phases/09-admin-panel-rearchitecture/09-03-PLAN.md
index 6ec0cc5..82d8b43 100644
--- a/.planning/phases/09-admin-panel-rearchitecture/09-03-PLAN.md
+++ b/.planning/phases/09-admin-panel-rearchitecture/09-03-PLAN.md
@@ -167,7 +167,7 @@ Verify before extracting each file: `grep -E "^import" frontend/src/components/a
- ADMIN-08: Four standalone admin view files exist under `frontend/src/views/admin/`.
- ADMIN-10: Each view is a self-contained component that the router can mount as a child of `/admin` (router wiring lands in 09-04).
- Behavior preserved: every admin CRUD/filter/download interaction continues to work after wiring in 09-04.
-
+