Context. surface tenant-admin · domain governance · route /content · auth signed-in (tenant) + scope:tenant:admin:{tenantId} (or tenant:admin:*) · source apps/oshun/tenant-admin/src/app/content/page.tsx
Last walked. —
Purpose#
Shows the effective tenant visibility (allow/deny) for content objects —
courses, personas, briefings, sources — after resolving an inherited district
policy plus tenant-level overrides via bulkResolveScope. It is the content
workspace (group: configuration) in @oshun/navigation.
Entry points#
- Shell sidebar Configuration › Content item (
TenantSidebar,shortLabel: 'Content', path/content). - Direct URL
/content(bookmarkable; scope-gated).
Layout regions#
Wrapped in <TenantShell session currentWorkspaceId="content">:
- Sidebar / Header: standard chrome (header crumb "Content scopes").
- Main
(
<section data-testid="tenant-content-panel" data-tenant-id={...}>):<h1>Content scopes</h1>+ subtitle on inherited district policy + tenant overrides.styles.summaryGridof three tiles: Allowed (count ofr.visibility === 'allow'), Denied (count of'deny'), Overrides (schoolScope.entries.length= 2).styles.tablecolumns Object / Kind / Effective visibility, one row per resolved object; visibility renders abadgeGood/badgeBadbadge.
States#
- Loading — N/A; synchronous server render from
bulkResolveScope()over hard-coded district + school scope chains (no fetch). - Empty — N/A; four objects are always resolved
(
metis-civic-reasoning,metis-socratic-guide,q2-exam-prep,unapproved-archive). - Populated (short) — always 4 rows. The chain resolves district-allow
then tenant override (e.g.
metis-socratic-guideflips todenyat the school scope;unapproved-archivefalls through todefaultPolicy: deny). - Populated (long) — N/A; fixed 4-object dataset; no pagination.
- Error (recoverable) — N/A; no query.
- Error (unrecoverable) — no custom handler; render throw → Next default.
- Offline — N/A (server-rendered).
- Gated — no session →
redirect('/unauthorized?reason=missing-session&returnTo=/content'); middleware blocks unauthenticated requests upstream. - Standalone PWA — N/A; no manifest. (verify.)
Interactions#
- Summary tiles (
styles.summaryItem× 3) — display-only counts (Allowed / Denied / Overrides); not focusable, no telemetry. - Resolved-object table rows (
<tr>keyed byobjectKind:objectId)- Function: render-only; shows
objectId(mono<code>),objectKind, and a visibility badge. No toggle/override-edit controls exist in code — read-only. - Keyboard: table is not interactive.
- Screen reader: standard table semantics.
- Touch target: N/A.
- Mobile (≤ 640 px):
workspace.module.csstable styling. - Disabled when: N/A.
- Offline behavior: N/A.
- Telemetry: None.
- Function: render-only; shows
Data & contracts#
- Reads:
getTenantServerSession();bulkResolveScope({ chain, objects })from@oshun/tenant-console— input is aTenantContentScope[]precedence chain (district → school) plus the list of objects to resolve; returns each object's effectivevisibility. Both scopes and the object list are hard-coded in the page. No BFF call. - Writes: None.
- Realtime: None.
- Caching: SSR (dynamic; cookie read). Deterministic per tenant id.
- Auth/role check:
middleware.ts+ pagegetTenantServerSession()→canEnterTenantShell.
Cross-references#
- Domain logic:
libs/oshun/tenant-console/src(bulkResolveScope,TenantContentScope). Index:libs/oshun/tenant-console/src/index.ts. - Workspace model:
libs/oshun/navigation/src/tenant-ia.ts(content,group: configuration). - Sibling routes: home.md, agents.md.
/policy(inherited bundles + tighten-only overrides) lacks a file yet.
Open questions / known gaps#
- District + school scope chains and the object list are hard-coded seed data; no BFF read of the tenant's real content-scope policy is wired. Document the eventual backing store and override-write path.
- Page is read-only — no UI to add/edit allow/deny overrides. Confirm whether editing is in scope for a later phase.