---
path: /review/[reviewId]
surface: admin
domain: governance
auth: signed-in (admin) + scope:admin:workspace:review
source: apps/oshun/admin/src/app/review/[reviewId]/page.tsx
status: drafted
last_walked: '—'
---

# Governance · Review package detail

## 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

- `/review` list — clicking a package row in `ReviewPackageList` or the
  keyboard-triage surface routes here
- Inbox row → review handoff (when the row's `originUrl` points at
  `/review/<id>`)
- Action rail "next/previous" on the detail page itself
  (`ReviewDetailActionRail` walks `queueIds`)
- Deep link / bookmark — the `reviewId` is 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 when
    `review.pendingHighRiskApproval !== null`
  - `<ReviewDelegationActions reviewId currentStage escalated assignedReviewerId reviewers>`
  - `<ReviewStageGraph currentState decisionOutcome>`
  - `<ReviewStageHistory entries>`
  - `<ReviewHistoryInspector entries integrityHash>` — integrity hash via
    `computeReviewStageHistoryIntegrityHash(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 from
  `snapshot.queue` or null
- Unauthorized redirect preserves the raw form via `encodeURIComponent` so
  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 except
      `ReviewHighRiskApprovalPanel` render
- [ ] Scope ok + `review.pendingHighRiskApproval !== null` →
      `ReviewHighRiskApprovalPanel` renders below the copilot
- [ ] Scope ok + escalated / delegated package → delegation actions reflect
      `escalated` / `assignedReviewerId` props
- [ ] `currentOperatorId` null (operator view missing) →
      `WorkspaceEntityComments` receives 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
      `pendingHighRiskApproval` requires it; uses `currentOperatorId` to 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`

## Data & contracts

- **Reads**: `loadWorkspaceDetail('review', session)` →
  `/v1/admin/workspaces/review`; package looked up in `snapshot.queue` by
  `reviewId`; integrity hash computed locally via
  `computeReviewStageHistoryIntegrityHash`
- **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 the
  `ReviewHighRiskApprovalPanel` performs additional `currentOperatorId`
  comparison

## 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` (per
  `OSHUN_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 lets
      `ReviewHistoryInspector` verify
- [ ] Document the contract for `ReviewCopilotProvider` (what state / actions it
      shares across the embedded copilot + decision panels)
- [ ] Confirm whether the page reads `reviewId` directly from the URL elsewhere
      (the action rail's `queueIds` array prevents desync, but the snapshot is
      server-fetched per nav)
