docs(05-employee-design): create gap closure plan for RBAC and error handling fixes

This commit is contained in:
2026-03-24 20:50:30 -06:00
parent 969cc4f917
commit b287a95014
2 changed files with 185 additions and 2 deletions

View File

@@ -103,12 +103,13 @@ Plans:
3. A template-deployed agent is immediately functional — responds in connected channels with the template's persona, tools, and escalation rules 3. A template-deployed agent is immediately functional — responds in connected channels with the template's persona, tools, and escalation rules
4. The wizard and templates are accessible to both platform admins and customer admins (respecting RBAC) 4. The wizard and templates are accessible to both platform admins and customer admins (respecting RBAC)
5. Created agents appear in the Agent Designer for further customization after initial setup 5. Created agents appear in the Agent Designer for further customization after initial setup
**Plans**: 3 plans **Plans**: 4 plans
Plans: Plans:
- [ ] 05-01-PLAN.md — Backend: AgentTemplate model, migration 007 with 7 seed templates, template list/deploy API, system prompt builder, unit + integration tests - [ ] 05-01-PLAN.md — Backend: AgentTemplate model, migration 007 with 7 seed templates, template list/deploy API, system prompt builder, unit + integration tests
- [ ] 05-02-PLAN.md — Frontend: three-option entry screen, template gallery with one-click deploy, 5-step wizard (Role/Persona/Tools/Channels/Escalation), Advanced mode relocation - [ ] 05-02-PLAN.md — Frontend: three-option entry screen, template gallery with one-click deploy, 5-step wizard (Role/Persona/Tools/Channels/Escalation), Advanced mode relocation
- [ ] 05-03-PLAN.md — Human verification: test all three creation paths, RBAC enforcement, system prompt auto-generation - [ ] 05-03-PLAN.md — Human verification: test all three creation paths, RBAC enforcement, system prompt auto-generation
- [ ] 05-04-PLAN.md — Gap closure: add /agents/new to proxy RBAC restrictions, hide New Employee button for operators, fix wizard deploy error handling
## Progress ## Progress
@@ -121,7 +122,7 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5
| 2. Agent Features | 6/6 | Complete | 2026-03-24 | | 2. Agent Features | 6/6 | Complete | 2026-03-24 |
| 3. Operator Experience | 5/5 | Complete | 2026-03-24 | | 3. Operator Experience | 5/5 | Complete | 2026-03-24 |
| 4. RBAC | 3/3 | Complete | 2026-03-24 | | 4. RBAC | 3/3 | Complete | 2026-03-24 |
| 5. Employee Design | 3/3 | Complete | 2026-03-25 | | 5. Employee Design | 3/4 | Gap Closure | 2026-03-25 |
--- ---

View File

@@ -0,0 +1,182 @@
---
phase: 05-employee-design
plan: 04
type: execute
wave: 1
depends_on: []
files_modified:
- packages/portal/proxy.ts
- packages/portal/app/(dashboard)/agents/page.tsx
- packages/portal/components/wizard-steps/step-review.tsx
autonomous: true
gap_closure: true
requirements: [EMPL-04]
must_haves:
truths:
- "customer_operator is redirected away from /agents/new (and all sub-paths) by proxy.ts before reaching creation UI"
- "customer_operator does not see the New Employee button on the agents list page"
- "Wizard deploy failure displays a visible error message to the user"
artifacts:
- path: "packages/portal/proxy.ts"
provides: "RBAC redirect for /agents/new paths"
contains: "/agents/new"
- path: "packages/portal/app/(dashboard)/agents/page.tsx"
provides: "Role-gated New Employee button"
contains: "useSession"
- path: "packages/portal/components/wizard-steps/step-review.tsx"
provides: "Visible error handling on deploy failure"
key_links:
- from: "packages/portal/proxy.ts"
to: "CUSTOMER_OPERATOR_RESTRICTED"
via: "/agents/new added to restricted array"
pattern: '"/agents/new"'
- from: "packages/portal/components/wizard-steps/step-review.tsx"
to: "error UI div"
via: "re-thrown error sets createAgent.isError"
pattern: "throw"
---
<objective>
Close two verification gaps from Phase 5 Employee Design:
1. Frontend RBAC gap: customer_operator can navigate to /agents/new and sub-paths (proxy.ts missing restriction) and sees the New Employee button (no role guard)
2. Wizard deploy error handling: catch block swallows errors so the error UI never renders
Purpose: Complete EMPL-04 compliance (RBAC-enforced access) and fix silent deploy failure UX
Output: Three patched files — proxy.ts, agents/page.tsx, step-review.tsx
</objective>
<execution_context>
@/home/adelorenzo/.claude/get-shit-done/workflows/execute-plan.md
@/home/adelorenzo/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/05-employee-design/05-VERIFICATION.md
<interfaces>
<!-- Key code the executor needs to patch. Extracted from codebase. -->
From packages/portal/proxy.ts (line 23):
```typescript
const CUSTOMER_OPERATOR_RESTRICTED = ["/billing", "/settings/api-keys", "/users", "/admin"];
```
From packages/portal/app/(dashboard)/agents/page.tsx (line 74):
```typescript
<Button onClick={() => router.push("/agents/new")}>
<Plus className="h-4 w-4 mr-2" />
New Employee
</Button>
```
From packages/portal/components/wizard-steps/step-review.tsx (lines 28-53):
```typescript
const handleDeploy = async () => {
try {
const agent = await createAgent.mutateAsync({
tenantId,
data: { /* ... */ },
});
router.push(`/agents/${agent.id}?tenant=${tenantId}`);
} catch (err) {
console.error("Failed to deploy agent:", err);
}
};
```
Error display div (lines 141-145):
```typescript
{createAgent.error && (
<div className="rounded-md bg-destructive/10 border border-destructive/20 p-3">
<p className="text-sm text-destructive">{createAgent.error.message}</p>
</div>
)}
```
Session pattern used in portal:
```typescript
import { useSession } from "next-auth/react";
const { data: session } = useSession();
const role = (session?.user as { role?: string })?.role;
```
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Add /agents/new to proxy RBAC restrictions and hide New Employee button for operators</name>
<files>packages/portal/proxy.ts, packages/portal/app/(dashboard)/agents/page.tsx</files>
<action>
1. In proxy.ts, add "/agents/new" to the CUSTOMER_OPERATOR_RESTRICTED array (line 23). The existing startsWith check on line 59 already handles sub-paths, so adding "/agents/new" will automatically block /agents/new/templates, /agents/new/wizard, and /agents/new/advanced.
2. In agents/page.tsx, add role-based visibility to the New Employee button:
- Import useSession from "next-auth/react"
- Get session via useSession() hook
- Extract role: `const role = (session?.user as { role?: string })?.role`
- Wrap the New Employee Button in a conditional: only render when role is "platform_admin" or "customer_admin" (i.e., hide when role is "customer_operator" or undefined)
- Use: `{role && role !== "customer_operator" && (<Button ...>)}`
Do NOT change any other behavior. The button still navigates to /agents/new. The proxy redirect is the security layer; the button hide is UX polish.
</action>
<verify>
<automated>cd /home/adelorenzo/repos/konstruct/packages/portal && grep -q '"/agents/new"' proxy.ts && grep -q 'useSession' app/\(dashboard\)/agents/page.tsx && echo "PASS"</automated>
</verify>
<done>customer_operator is redirected by proxy.ts when navigating to /agents/new or any sub-path; New Employee button is hidden for customer_operator role</done>
</task>
<task type="auto">
<name>Task 2: Fix wizard deploy error handling to surface errors to user</name>
<files>packages/portal/components/wizard-steps/step-review.tsx</files>
<action>
In step-review.tsx, fix the handleDeploy catch block (lines 50-52) to re-throw the error so TanStack Query's mutateAsync sets the mutation's isError/error state. This allows the existing error display div at lines 141-145 to render.
Change the catch block from:
```typescript
} catch (err) {
console.error("Failed to deploy agent:", err);
}
```
To:
```typescript
} catch (err) {
console.error("Failed to deploy agent:", err);
throw err;
}
```
This is the minimal fix. The mutateAsync call throws on error; catching without re-throwing prevents TanStack Query from updating mutation state. Re-throwing lets createAgent.error get set, which triggers the existing error div to display.
Do NOT add useState for local error handling — the existing createAgent.error UI is correctly wired, it just never receives the error.
</action>
<verify>
<automated>cd /home/adelorenzo/repos/konstruct/packages/portal && grep -A2 'catch (err)' components/wizard-steps/step-review.tsx | grep -q 'throw err' && echo "PASS"</automated>
</verify>
<done>Deploy failures in wizard now surface error message to user via the existing error display div; createAgent.isError becomes true on failure</done>
</task>
</tasks>
<verification>
1. grep for "/agents/new" in CUSTOMER_OPERATOR_RESTRICTED array in proxy.ts
2. grep for useSession import in agents/page.tsx
3. grep for "throw err" in step-review.tsx catch block
4. Confirm no other files were modified
</verification>
<success_criteria>
- proxy.ts CUSTOMER_OPERATOR_RESTRICTED includes "/agents/new"
- agents/page.tsx New Employee button conditionally rendered based on session role
- step-review.tsx catch block re-throws error so mutation error state is set
- All three changes are minimal, surgical fixes to close the two verification gaps
</success_criteria>
<output>
After completion, create `.planning/phases/05-employee-design/05-04-SUMMARY.md`
</output>