Source: apps/oshun/admin/src/lib/server-session.ts (getAdminServerSession,
fetchAdminOperatorView), apps/oshun/admin/src/lib/workspace-loader.ts
(loadWorkspaceOverview, loadWorkspaceDetail),
apps/oshun/admin/src/lib/bff-client.ts (AdminBffFetchResult,
AdminBffWorkspaceSummary, AdminBffWorkspaceDetailResponse),
apps/oshun/admin/src/components/WorkspaceEntryPoint.tsx,
apps/oshun/admin/src/components/WorkspaceSummaryCard.tsx,
apps/oshun/admin/src/components/WorkspacePage.tsx,
libs/oshun/navigation/src/admin-ia.ts (OSHUN_ADMIN_WORKSPACE_MODEL)
The shape every admin workspace page follows. Walk this once; then per-view files focus only on what's unique to each workspace.
Canonical page shape#
export default async function FooPage(): Promise<JSX.Element> {
const session = await getAdminServerSession();
if (!session) {
redirect('/unauthorized?reason=missing-session&returnTo=/foo');
}
const [detail, operatorView] = await Promise.all([
loadWorkspaceDetail('foo', session),
fetchAdminOperatorView(session),
]);
return (
<AdminShell session={session} currentWorkspaceId="foo" operator={operatorView.view}>
<FooPanel detail={detail} ... />
{/* or fallback */}
<WorkspaceEntryPoint workspaceId="foo" accessible={detail.accessible} detail={detail.result} />
</AdminShell>
);
}
Three things happen on every page render:
- Server session check —
getAdminServerSession()returns null if no valid admin cookie; page redirects to/unauthorizedwith reason- returnTo.
- Parallel data fetch —
loadWorkspaceDetail(workspaceId, session)for workspace data +fetchAdminOperatorView(session)for the operator's display profile. - Render —
<AdminShell>wraps the workspace body. The body is either a workspace-specific panel (when data is available and the operator has access) or<WorkspaceEntryPoint>(which itself renders an "Access not granted" or "Workspace data unavailable" notice).
getAdminServerSession()#
Returns AdminServerSession | null. When non-null:
session.session.payload.userId— operator user idsession.session.payload.scopes— array of scopessession.canEnterShell— boolean used by assistant guards
Null means no valid admin cookie. Pages handle this by redirecting, not by
rendering a denial UI inline — that's the middleware//unauthorized role.
loadWorkspaceDetail(workspaceId, session)#
Returns an AdminBffFetchResult<AdminBffWorkspaceDetailResponse>:
{ ok: true, value: { workspace: AdminBffWorkspaceSummary, items: …, metrics: … } }{ ok: false, reason: 'workspace-unavailable' | 'forbidden' | 'network' | …, message?: string }
Plus an accessible flag derived from
canEnterAdminWorkspace(session scopes, workspaceId).
For pages with no BFF backing (e.g., lilith workspace has
backendStatus: 'backend-pending'), result may be null and the page falls
back to <WorkspaceEntryPoint>'s "Workspace data unavailable" notice.
IA id vs BFF id — watch for drift#
Several workspaces' bffWorkspaceId does not match their IA id.
loadWorkspaceDetail(<iaId>, session) reads bffWorkspaceId off the workspace
definition and hits /v1/admin/workspaces/<bffId>, not the IA id. When you're
tracing a log line or a network panel, the request path will reference the BFF
id; the URL and sidebar reference the IA id. Use this table to translate.
| IA id (route + sidebar) | BFF id (request path) |
|---|---|
dashboard |
n/a (composed-from-workspaces) |
inbox |
inbox |
review |
review |
policy |
policy |
trust-safety |
moderation |
lilith |
n/a (backend-pending) |
rights |
rights |
incidents |
incident |
editorial |
editorial |
research-integrity |
research-integrity |
personas |
persona |
models |
model |
isis |
n/a (per-route loaders) |
support |
support |
privacy |
privacy |
analytics |
analytics |
admin-tools |
n/a (composed-from-workspaces) |
messaging |
n/a (backend-pending) |
tenant-console |
n/a (backend-pending) |
The four bolded rows differ between IA and BFF and have caused log-tracing
confusion in the past. The drift is now codified in
libs/oshun/navigation/src/admin-ia.ts as OSHUN_ADMIN_BFF_ID_DRIFT (an
explicit list of the four mismatched pairs) plus two derivation helpers (P3
2026-05-25):
resolveBffWorkspaceIdFromIaId(iaId)— call this whenever building a BFF URL or correlating a BFF call. Returnsundefinedfor non-BFF-backed workspaces.resolveAdminWorkspaceIdFromBffId(bffId)— reverse direction. Use this when reading raw BFF telemetry / audit logs and needing to surface the matching IA workspace label in the cockpit.
A contract test (admin-ia.test.ts) asserts the four declared drift pairs match
the IA model and that every other BFF-backed workspace has IA id === BFF id
(no hidden drift). Renaming the BFF ids to match the IA ids would make all four
pairs identity-mapped but requires a coordinated rewrite of
apps/oshun/bff/src/admin/state.ts; until that happens, the helpers are the
canonical translation layer — do not inline the conditional in call sites.
loadWorkspaceOverview(session)#
Returns the full multi-workspace summary for the dashboard. Used by / to
render WorkspaceSummaryCards for every workspace at once.
WorkspaceEntryPoint#
apps/oshun/admin/src/components/WorkspaceEntryPoint.tsx (110 lines)
The fallback / wrapper body. Renders:
- Heading block — h2 with
definition.label; tagline fromdefinition.operatorPromise;<dl>withGroup,Backend,Operator rolefromdefinition.backendStatusanddefinition.operatorRelationship -
WorkspaceSummaryCard— summary metrics from the BFF response; renders queue health, item count, last-updated, etc. Falls back to "no summary" ifresultis null -
accessible === falsepanel —role="alert"; heading "Access not granted"; body explains the required scopes (admin:*,admin:studio, or workspace-specific) and the ADR-0029 privileged handoff path -
accessible === true && !detail.okpanel —role="alert"; heading "Workspace data unavailable"; body eitherreason: 'workspace-unavailable'ordetail.message - Related handoffs block —
listOshunAdminWorkspaceRelationships()filtered tofrom === workspaceId; for each, shows target label, reason, trigger; heading "Handoffs from here"
States to walk:
- Accessible + data ok — full summary card renders
- Accessible + workspace-unavailable — "Workspace data unavailable" notice; rest of summary block still renders
- Accessible + network error —
detail.messagesurfaces - Not accessible — "Access not granted" alert; no summary card data shown
- Empty related handoffs — section omitted
WorkspaceSummaryCard#
apps/oshun/admin/src/components/WorkspaceSummaryCard.tsx
Renders the per-workspace summary tile. Used both inside WorkspaceEntryPoint
(one) and in the dashboard (many). Walk this component for its own states
(loading / empty / populated / error).
Workspace-specific panels#
Most workspaces have a dedicated panel component beyond WorkspaceEntryPoint.
Inventory (incomplete; per-view files fill in):
| Workspace | Panel component |
|---|---|
dashboard |
CopilotHealthPanel, WorkspaceSummaryCard ×N |
inbox |
UnifiedInboxPanel |
review |
(workspace panel TBD per per-view) |
review/[reviewId] |
review-detail workspace component |
trust-safety |
moderation queue panel |
| (others) | per-view files document the panel |
Per-view files own the per-workspace panel walkthrough. This shell doc covers
only the WorkspaceEntryPoint fallback path.
Operator copilot guard#
Every workspace inherits the assistant-invocation guard from AdminShell. The
guard input includes:
{
invocationPointId,
authenticated: session.canEnterShell,
activePath: window.location.pathname,
viewportWidthPx: window.innerWidth,
scopes: session.session.payload.scopes,
entitlements: ['assistant.admin'],
policyGrants: ['assistant:operations'],
}
The result becomes assistantGuardDecision. If denied, the assistant panel
shows the block reason. See 01-app-shell.md for the full
guard composition.
States that apply to every workspace page#
- Anonymous — middleware redirects; never reaches the page
- Admin session, missing workspace scope —
accessible: false;<WorkspaceEntryPoint>renders denial panel - Admin session, workspace scope, BFF ok — workspace-specific panel renders with data
- Admin session, scope ok, BFF returns
workspace-unavailable— "Workspace data unavailable" notice - Admin session, scope ok, BFF returns network error — error message via
detail.message -
backendStatus: 'backend-pending'workspace — no BFF call; always shows the entry-point fallback (Lilith is the canonical example today)
Cross-references#
- 01-app-shell.md — AdminShell composition
- 02-routing-layouts.md — middleware that enforces the session before the page runs
- 03-auth-session.md — scopes and access matrix
- Workspace definitions:
libs/oshun/navigation/src/admin-ia.ts - BFF client:
apps/oshun/admin/src/lib/bff-client.ts
Open questions / known gaps#
- Inventory every workspace's dedicated panel component (when one exists
beyond
WorkspaceEntryPoint) and link from each per-view file - Document the BFF endpoint pattern (
/v1/admin/workspaces/<id>based on the inbox example) and which workspaces it covers - Confirm
forbidden-workspacereason originates from a page-level check, not from middleware (middleware doesn't read scopes today) - Document how
WorkspaceSummaryCardbehaves when summary is partial (some fields present, others missing)