diff --git a/frontend/src/components/layout/ProtectedRoute.tsx b/frontend/src/components/layout/ProtectedRoute.tsx
index 184df619..8b364518 100644
--- a/frontend/src/components/layout/ProtectedRoute.tsx
+++ b/frontend/src/components/layout/ProtectedRoute.tsx
@@ -30,6 +30,24 @@ export function ProtectedRoute({ requiredRole, children }: ProtectedRouteProps)
return
}
+ // L1 techs are confined to their focused surface. The sidebar only exposes
+ // /l1*, /guides, and /account for them, so any other authed path (the engineer
+ // dashboard at /home, /pilot, /trees/*, /escalations, …) bounces to /l1. This
+ // also covers post-login landing: auth sends users to /home, which is not in
+ // the allowlist, so l1_tech users end up on /l1. Engineer-only AI surfaces
+ // (/pilot, /assistant) would 403 at POST /api/v1/ai-sessions anyway — this
+ // turns that backend error into a clean redirect. Runs before the requiredRole
+ // check so L1 users never trip the engineer-route role logic.
+ if (effectiveRole === 'l1_tech') {
+ const L1_ALLOWED_PREFIXES = ['/l1', '/guides', '/account', '/change-password']
+ const allowed = L1_ALLOWED_PREFIXES.some(
+ (p) => location.pathname === p || location.pathname.startsWith(p + '/'),
+ )
+ if (!allowed) {
+ return
+ }
+ }
+
if (requiredRole) {
const ROLE_HIERARCHY: Record = {
super_admin: 5,
@@ -43,23 +61,6 @@ export function ProtectedRoute({ requiredRole, children }: ProtectedRouteProps)
}
}
- // L1 users landing on / (e.g. post-login) get redirected to their workspace.
- // Does not fire when already on /l1 or any other path, preventing loops.
- if (effectiveRole === 'l1_tech' && location.pathname === '/') {
- return
- }
-
- // L1 users hitting engineer-only AI surfaces (Pilot / Assistant) get pushed
- // back to /l1 — POST /api/v1/ai-sessions rejects them with 403 anyway, so
- // this just turns a backend error into a clean route-level redirect.
- if (
- effectiveRole === 'l1_tech' &&
- (location.pathname.startsWith('/pilot') ||
- location.pathname.startsWith('/assistant'))
- ) {
- return
- }
-
return <>{children}>
}
diff --git a/frontend/src/components/layout/Sidebar.tsx b/frontend/src/components/layout/Sidebar.tsx
index d97830a8..aaca8061 100644
--- a/frontend/src/components/layout/Sidebar.tsx
+++ b/frontend/src/components/layout/Sidebar.tsx
@@ -256,6 +256,7 @@ export function Sidebar() {
: 'text-text-rail-label hover:text-foreground'
)}
title={item.label}
+ aria-label={item.label}
>