fix(rls): add account_id to AISessionStep creations, fix boards toast
- flowpilot_engine: pass account_id at all 5 AISessionStep instantiation sites (_create_step_from_parsed x3, briefing step, status update step). Phase 4 RLS blocked every INSERT with NULL account_id — this broke all new FlowPilot sessions since the Phase 4 migration was applied. - integrations: list_boards returns [] on PSAError instead of 502, stopping the spurious 'Server error' toast on dashboard load (boards are optional). - client.ts: 5xx global toast now shows backend detail when available. - useFlowPilotSession: startSession extracts backend detail for error state; suppresses duplicate toast for 5xx (global interceptor already handles it). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -362,8 +362,9 @@ async def list_boards(
|
||||
provider = await get_provider_for_account(current_user.account_id, db)
|
||||
boards = await provider.list_boards()
|
||||
return [PSABoardResponse(id=b.id, name=b.name) for b in boards]
|
||||
except PSAError as e:
|
||||
raise HTTPException(status_code=502, detail=str(e))
|
||||
except PSAError:
|
||||
# Boards are optional UI chrome — degrade gracefully rather than surfacing a toast
|
||||
return []
|
||||
|
||||
|
||||
@router.get("/tickets/search", response_model=list[PSATicketSearchResult])
|
||||
|
||||
@@ -330,6 +330,7 @@ async def start_session(
|
||||
# 7. Create first step
|
||||
step = _create_step_from_parsed(
|
||||
session_id=session.id,
|
||||
account_id=session.account_id,
|
||||
step_order=0,
|
||||
parsed=parsed,
|
||||
input_tokens=input_tokens,
|
||||
@@ -433,6 +434,7 @@ async def process_response(
|
||||
# Create new step
|
||||
step = _create_step_from_parsed(
|
||||
session_id=session.id,
|
||||
account_id=session.account_id,
|
||||
step_order=session.step_count - 1,
|
||||
parsed=parsed,
|
||||
input_tokens=input_tokens,
|
||||
@@ -694,6 +696,7 @@ async def pickup_session(
|
||||
briefing_step = AISessionStep(
|
||||
id=uuid.uuid4(),
|
||||
session_id=session.id,
|
||||
account_id=session.account_id,
|
||||
branch_id=session.active_branch_id if session.is_branching else None,
|
||||
step_order=session.step_count,
|
||||
step_type="action",
|
||||
@@ -765,6 +768,7 @@ async def pickup_session(
|
||||
|
||||
next_step = _create_step_from_parsed(
|
||||
session_id=session.id,
|
||||
account_id=session.account_id,
|
||||
step_order=session.step_count - 1,
|
||||
parsed=parsed,
|
||||
input_tokens=input_tokens,
|
||||
@@ -997,6 +1001,7 @@ async def generate_status_update(
|
||||
step = AISessionStep(
|
||||
id=uuid.uuid4(),
|
||||
session_id=session.id,
|
||||
account_id=session.account_id,
|
||||
branch_id=session.active_branch_id if session.is_branching else None,
|
||||
step_order=session.step_count,
|
||||
step_type="status_update",
|
||||
@@ -1440,6 +1445,7 @@ def _format_engineer_response(request: StepResponseRequest) -> str:
|
||||
|
||||
def _create_step_from_parsed(
|
||||
session_id: UUID,
|
||||
account_id: UUID,
|
||||
step_order: int,
|
||||
parsed: dict[str, Any],
|
||||
input_tokens: int,
|
||||
@@ -1487,6 +1493,7 @@ def _create_step_from_parsed(
|
||||
return AISessionStep(
|
||||
id=uuid.uuid4(),
|
||||
session_id=session_id,
|
||||
account_id=account_id,
|
||||
branch_id=branch_id,
|
||||
step_order=step_order,
|
||||
step_type=step_type if parsed["type"] != "resolution_suggestion" else "action",
|
||||
|
||||
@@ -49,9 +49,9 @@ function handleGlobalError(error: AxiosError) {
|
||||
return
|
||||
}
|
||||
|
||||
// Server errors (5xx)
|
||||
// Server errors (5xx) — show backend detail when available, else generic message
|
||||
if (status >= 500) {
|
||||
toast.error('Server error - please try again later')
|
||||
toast.error(detail || 'Server error - please try again later')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,9 +99,14 @@ export function useFlowPilotSession(): UseFlowPilotSession {
|
||||
setAllSteps([firstStep])
|
||||
setCurrentStep(firstStep)
|
||||
} catch (e: unknown) {
|
||||
const message = e instanceof Error ? e.message : 'Failed to start session'
|
||||
// Prefer the backend's detail message over the generic axios status string
|
||||
const detail = (e as any)?.response?.data?.detail
|
||||
const message = typeof detail === 'string' ? detail : (e instanceof Error ? e.message : 'Failed to start session')
|
||||
setError(message)
|
||||
toast.error(message)
|
||||
// Global axios interceptor already shows a toast for 5xx — skip duplicate
|
||||
if (!(e as any)?.response?.status || (e as any)?.response?.status < 500) {
|
||||
toast.error(message)
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user