---
phase: 05-employee-design
plan: "04"
subsystem: portal
tags: [rbac, ux, bugfix, gap-closure]
dependency_graph:
  requires: [05-03]
  provides: [EMPL-04-complete]
  affects: [proxy.ts, agents-page, wizard-deploy]
tech_stack:
  added: []
  patterns: [useSession role gate, proxy RBAC restriction, TanStack Query error re-throw]
key_files:
  created: []
  modified:
    - packages/portal/proxy.ts
    - packages/portal/app/(dashboard)/agents/page.tsx
    - packages/portal/components/wizard-steps/step-review.tsx
decisions:
  - "/agents/new added to CUSTOMER_OPERATOR_RESTRICTED — startsWith check already covers all sub-paths (wizard, templates, advanced)"
  - "Button hidden with role guard in addition to proxy redirect — security at proxy, UX polish at component"
  - "catch re-throw is minimal fix — existing createAgent.error UI was correctly wired, just never received the error"
metrics:
  duration: "~1 min"
  completed: "2026-03-25"
  tasks: 2
  files: 3
requirements: [EMPL-04]
---

# Phase 5 Plan 4: RBAC Gap Closure and Wizard Error Fix Summary

**One-liner:** Closed two verification gaps — proxy RBAC blocks /agents/new for operators and wizard deploy errors now surface to user via TanStack Query mutation state.

## Tasks Completed

| Task | Name | Commit | Files |
|------|------|--------|-------|
| 1 | Add /agents/new to proxy RBAC restrictions and hide New Employee button | 8b697aa | proxy.ts, agents/page.tsx |
| 2 | Fix wizard deploy error handling to surface errors to user | 67b3690 | step-review.tsx |

## What Was Built

### Task 1: Frontend RBAC Gap Closure

Two changes to close the operator access gap for agent creation:

**proxy.ts** — Added `"/agents/new"` to `CUSTOMER_OPERATOR_RESTRICTED` array. The existing `startsWith` check at line 59 automatically extends protection to all sub-paths (`/agents/new/templates`, `/agents/new/wizard`, `/agents/new/advanced`). No additional logic needed.

**agents/page.tsx** — Added `useSession` import from `next-auth/react`, extracted `role` from session, and wrapped the New Employee button in a conditional render: `{role && role !== "customer_operator" && (<Button ...>)}`. The button is hidden entirely for operators — the proxy redirect is the security enforcement; button hiding is UX polish to avoid visible-but-blocked affordances.

### Task 2: Wizard Deploy Error Fix

**step-review.tsx** — Added `throw err` in the catch block of `handleDeploy`. The `mutateAsync` call throws on failure; catching without re-throwing caused TanStack Query to never update `createAgent.error` or `createAgent.isError`. The existing error display div at lines 141-145 was correctly wired — it simply never received the error. Re-throwing allows the mutation state to update, and the error div renders automatically.

## Deviations from Plan

None — plan executed exactly as written.

## Success Criteria Verification

- [x] proxy.ts CUSTOMER_OPERATOR_RESTRICTED includes "/agents/new"
- [x] agents/page.tsx New Employee button conditionally rendered based on session role
- [x] step-review.tsx catch block re-throws error so mutation error state is set
- [x] All three changes are minimal, surgical fixes — only 3 files modified, exactly as specified

## Self-Check: PASSED

Files exist:
- packages/portal/proxy.ts — FOUND
- packages/portal/app/(dashboard)/agents/page.tsx — FOUND
- packages/portal/components/wizard-steps/step-review.tsx — FOUND

Commits exist:
- 8b697aa — FOUND (feat: RBAC restriction + button hide)
- 67b3690 — FOUND (fix: re-throw deploy error)
