Context. surface admin · domain governance · route /review/[reviewId] · auth signed-in (admin) + scope:admin:workspace:review · source apps/oshun/admin/src/app/review/[reviewId]/page.tsx
Last walked. —
Purpose#
Per-package review cockpit: shows the artifact, stage graph, audit timeline,
copilot suggestion + feedback, decision actions, delegation / escalation,
high-risk approval, comments, links, and the investigation-bundle exporter for a
single review package identified by [reviewId].
Entry points#
/reviewlist — clicking a package row inReviewPackageListor the keyboard-triage surface routes here- Inbox row → review handoff (when the row's
originUrlpoints at/review/<id>) - Action rail "next/previous" on the detail page itself
(
ReviewDetailActionRailwalksqueueIds) - Deep link / bookmark — the
reviewIdis URI-decoded from the dynamic segment (decodeURIComponent(rawReviewId))
Layout regions#
Inside AdminShell (currentWorkspaceId="review"):
<WorkspaceEntryPoint workspaceId="review" ...>— always rendered first- When
detail.accessible && review === null(id not in current snapshot):<section data-testid="review-detail-not-found">— h3 "Review package not found"; copy explaining the package may have been closed/archived/routed;<Link href="/review">← Back to review packages</Link> - When
detail.accessible && review !== null, wrapped in<ReviewCopilotProvider>:<ReviewDetailActionRail queueIds={queueIds} currentReviewId>— prev/next nav within the snapshot queue<ReviewPackageDetail review reviewers assignedReviewer>— body<ReviewAuditTimelinePanel stageHistory auditEvents><ReviewTemplatePanel artifactClass artifactRef><ReviewBlockersPanel blockers><ReviewDecisionCopilot reviewId artifactRef currentStage decisionOutcome suggestion feedback><ReviewDecisionActions reviewId artifactRef currentStage decisionOutcome copilotSuggestion><ReviewHighRiskApprovalPanel>— only whenreview.pendingHighRiskApproval !== null<ReviewDelegationActions reviewId currentStage escalated assignedReviewerId reviewers><ReviewStageGraph currentState decisionOutcome><ReviewStageHistory entries><ReviewHistoryInspector entries integrityHash>— integrity hash viacomputeReviewStageHistoryIntegrityHash(stageHistory)<ReviewAuditEventLog events><WorkspaceEntityLinks workspaceId="review" entityId entityLabel><WorkspaceEntityComments workspaceId="review" entityId entityLabel currentOperatorId><InvestigationBundleExporter workspaceId="review" entityId entityLabel>
[reviewId] param contract#
- Dynamic segment is URI-decoded —
reviewId = decodeURIComponent(rawReviewId) findReviewPackage(snapshot, reviewId)returns the matching package fromsnapshot.queueor null- Unauthorized redirect preserves the raw form via
encodeURIComponentso middleware sends the operator back to the same URL after handoff - Drives
findReviewerProfile(snapshot, review.assignedReviewerId)for the assigned-reviewer card
States#
- Anonymous → middleware redirect to
/unauthorized?reason=missing-session&returnTo=/review/<id>(URL-encoded) - Signed-in (admin) without scope →
WorkspaceEntryPoint"Access not granted"; detail panels hidden - Scope ok + reviewId not found in snapshot → "Review package not found" notice with back link
- Scope ok + reviewId found,
decisionOutcome === null,pendingHighRiskApproval === null→ all panels exceptReviewHighRiskApprovalPanelrender - Scope ok +
review.pendingHighRiskApproval !== null→ReviewHighRiskApprovalPanelrenders below the copilot - Scope ok + escalated / delegated package → delegation actions reflect
escalated/assignedReviewerIdprops -
currentOperatorIdnull (operator view missing) →WorkspaceEntityCommentsreceives null; high-risk panel still receives null
Interactions#
The page is a composition; discrete interactive elements live inside each panel:
- ReviewDetailActionRail — prev/next package nav (within
queueIds) - ReviewDecisionActions — approve / request-changes / reject etc. (write back to the review entity)
- ReviewDecisionCopilot — surface copilot suggestion, record feedback
(
accept/override/ignore) tied to V1-AWEB-064 - ReviewDelegationActions — delegate to a reviewer in
snapshot.reviewers; escalate - ReviewHighRiskApprovalPanel — secondary signoff when
pendingHighRiskApprovalrequires it; usescurrentOperatorIdto gate self-approval - WorkspaceEntityComments — comment thread tied to entityId
- InvestigationBundleExporter — exports the audit / artifact bundle for the package
- "← Back to review packages" (link, only on not-found state)
- Function: navigates to
/review
- Function: navigates to
Data & contracts#
- Reads:
loadWorkspaceDetail('review', session)→/v1/admin/workspaces/review; package looked up insnapshot.queuebyreviewId; integrity hash computed locally viacomputeReviewStageHistoryIntegrityHash - Writes: per panel (decide, delegate, comment, link, export); endpoints are owned by each component
- Realtime: None at the page level.
- Auth/role check: middleware + page
(
canEnterAdminWorkspace(scopes, 'review')); within the page theReviewHighRiskApprovalPanelperforms additionalcurrentOperatorIdcomparison
Cross-references#
- Shell:
shell/01-app-shell.md,shell/04-workspace-pattern.md - Workspace definition:
libs/oshun/navigation/src/admin-ia.ts(review) - Parent route:
review.md - Handoff destinations from
review:../safety/rights.md,../content/personas.md,../content/models.md(perOSHUN_ADMIN_WORKSPACE_RELATIONSHIPS) - Component sources (all under
apps/oshun/admin/src/components/):ReviewPackageDetail.tsx,ReviewDecisionActions.tsx,ReviewDecisionCopilot.tsx,ReviewDelegationActions.tsx,ReviewHighRiskApprovalPanel.tsx,ReviewStageGraph.tsx,ReviewStageHistory.tsx,ReviewHistoryInspector.tsx,ReviewAuditEventLog.tsx,ReviewAuditTimelinePanel.tsx,ReviewDetailActionRail.tsx,ReviewTemplatePanel.tsx,ReviewBlockersPanel.tsx,WorkspaceEntityLinks.tsx,WorkspaceEntityComments.tsx,InvestigationBundleExporter.tsx,ReviewCopilotProvider.tsx - Library:
apps/oshun/admin/src/lib/review-detail.ts
Open questions / known gaps#
- Document the integrity hash algorithm
(
computeReviewStageHistoryIntegrityHash) and what it letsReviewHistoryInspectorverify - Document the contract for
ReviewCopilotProvider(what state / actions it shares across the embedded copilot + decision panels) - Confirm whether the page reads
reviewIddirectly from the URL elsewhere (the action rail'squeueIdsarray prevents desync, but the snapshot is server-fetched per nav)