---
title: "SEVIS Ready Pool Review Workspace"
slug: sevis-ready-pool-review-workspace
section: Rules & Compliance
status: stable
description: "The operational review workspace in the BackOfficePrototype SEVIS Ready Pool lets staff inspect server-resolved evidence for a Ready applicant and record a human review decision that never changes the readiness verdict."
last-updated: 2026-10-07
---

# SEVIS Ready Pool Review Workspace

The operational review workspace in the BackOfficePrototype SEVIS Ready Pool lets staff inspect server-resolved evidence for a Ready applicant and record a human review decision that never changes the readiness verdict.

## Overview

Every Ready Pool row can open a per-application workspace. The workspace reads one endpoint, `GET /api/applications/:id/sevis-review`, which assembles identity and passport fields, placement and contract terms, linked documents, the persisted readiness ledger, eligibility and screening evidence, and any active override signal. A reviewer then records one of three decisions (`reviewed`, `needs_attention`, `reopen`) through `POST /api/applications/:id/sevis-review/decisions`.

Two separate things are tracked for each applicant and must not be confused:

| Concept | Source | Who changes it |
| --- | --- | --- |
| Readiness verdict | Stored SEVIS readiness projection (see [SEVIS Readiness Criteria](/docs/sevis-readiness)) | The evaluator and sweeper, never a reviewer |
| Operational review status | Review work item and decision history | A staff reviewer with the manage permission |

> **Review is a human outcome, not a verdict**
>
> A review decision is stored as a review work item transition. It does not write to the readiness projection and cannot make a Not Ready applicant Ready. The decision endpoint additionally refuses to record anything unless the stored verdict is readable, current and `ready`. [1] [2]

## Workspace Layout

The workspace component is `SevisReviewWorkspace`. When expanded it renders as a full-screen panel with a **Close review** button and locks body scrolling until closed; scroll position is restored on close. The review query is enabled only while the workspace or the evidence drawer is open, with a 30 second stale time. [3] [4]

### Tabs

| Tab | Shown when | Content |
| --- | --- | --- |
| SEVIS Review | Only when the application's gate state is Ready | Review summary, evidence, decision footer |
| SEVIS Readiness | Always | Stored readiness detail and active overrides |
| Notes | Always | Internal notes for the application |
| History | Always | Activity timeline filtered to the `sevis_visa` category |

The initial tab is SEVIS Review for a Ready application and SEVIS Readiness otherwise. The Notes tab lists up to eight notes, requires `applicant.profile.view` to read and `applicant.notes.edit` to add, and posts internal, general-category notes to `/api/applications/:id/notes`. [5]

### Evidence Drawer

Choosing an evidence item opens a right-hand drawer (expandable to 96 percent of viewport width) with the selected document, a document picker, and buttons for the blueprint's evidence purposes (passport, placement contract).

- **Queue navigation.** Previous and Next walk only applicants that pass the client-side Ready gate check, taken from a review queue fetched separately from the visible page. Navigation is disabled while a decision is pending.
- **Mark reviewed and Next.** In drawer mode a successful `reviewed` or `needs_attention` decision advances to the next Ready applicant in the queue; when none remains the drawer shows "Review completed".
- **Viewed markers.** Documents opened in the drawer are tracked per application in session state only. They are context for the reviewer, not a recorded pass.
- **Override notice.** If the readiness data carries effective override contributions, the drawer shows a notice that an active override contributes to the persisted outcome through a legacy SEVIS operational exception. [3]

### Document Reader

**Read document** opens a full-screen dialog. PDFs are fetched with the session's auth headers and rendered page by page to a canvas through `pdfjs-dist`, with Previous/Next page, zoom from 50 to 250 percent in 25 percent steps, Fit width and Download. The fetch aborts after 30 seconds. Word files (`.doc`, `.docx`) are routed through the server [Word Preview](#word-preview) endpoint and rendered the same way. Any other file type shows "This file cannot be previewed safely in the browser" with a Download button. [6]

## Permissions

| Capability | Requirement |
| --- | --- |
| Load the review payload | `sevis.ready_pool.view`, plus entity scope on the application's program, season and brand |
| Record a decision | `sevis.ready_pool.manage`, plus entity scope |
| Receive document lists, evidence links and package artifacts | `document.view` |
| Receive unmasked passport fields and non-citizenship comparison values | PII unmask permission (`canUnmaskPII`) |
| Receive override details (reason, actor, expiry) | `sevis.override.manage`; other callers get `{ status: "active" }` only |
| See the eligibility assessment panel | `compliance.console.view` |

The decision footer renders only for users holding `sevis.ready_pool.manage`. [1] [3]

## Review Data Contract

`getSevisOperationalReview` is the single orchestration boundary; routes do not read review tables directly. The response contains `review`, `sections`, `documents`, `blueprint`, `operationalEvidence`, `readiness`, `ruleOutcomes`, `activeOverrides` and `versions`. `GET` is read-only: when no review work item exists yet, a virtual item with status `pending` and `reviewVersion` 0 is returned without inserting a row. [2] [7]

The `readiness` envelope is `available` with freshness `fresh` or `stale` when a stored verdict is readable, and otherwise `unavailable` with freshness `pending`. The workspace never evaluates readiness while loading.

### Review Blueprint

The server chooses which sections apply from program configuration, so the React surface does not hard-code per-program layouts. [8]

| Program key or category code | Sections |
| --- | --- |
| `swt-camp`, `swt_camp`, `camp-counselor`, `camp_counselor`, and every key in `SWT_CAMP_PROGRAM_KEYS` | passport, placement, readiness |
| Anything else | passport, readiness |

All blueprints declare the readiness evidence code fragments `terms`, `criminal` and `background`, used to link documents to readiness checks.

### Sections and Evidence

- **Passport.** Fields (given names, surname, passport number, date of birth, nationality, expiry) come from the passport step snapshot with applicant-record fallbacks, and are returned as null when the caller lacks PII unmask. Identity comparisons (name, date of birth, citizenship) carry outcomes and sides; side values are redacted except for citizenship. The section also carries the passport rules, drawn from both passed checks and blockers, so a passed expiry check is not shown as unavailable.
- **Placement.** Host, job, status, approval (approver, timestamp, cancellation details), contract dates, duration in days and the contract comparison. Present only for blueprints that include placement.
- **Readiness.** The persisted category checks, each enriched with document references matched by check text (criminal and background, terms, passport and identity, contract and placement). Section status is `overridden` only when a persisted effective override contribution names that section; otherwise `fail`, `indeterminate`, `pass` or `unavailable`. A missing blocker is never read as a pass.
- **Compliance case references.** Each `complianceRuleComposition` requirement on the readiness data whose `source` is `compliance_override` gets `complianceCaseReferenceStatus` (`available` or `unavailable`) and a `complianceCase` object, or `null` when no case could be linked. A reference carries the case `number`, `itemId`, `decidedAt` and `decisionProvenance`, and is linked only when the case's recorded binding matches this application, program and season. `decisionProvenance` is `available` only for an approved case on an active override; it then holds the decision `reason`, the `approvers` (primary, secondary and decision approver, with role) and the supporting `evidence` documents (name, type, version, status, `availability` and `viewUrl`). Otherwise it is `unavailable` with empty approvers and evidence. `viewUrl` is set only when the caller holds `document.view` and the document version is available.
- **Operational evidence.** Eligibility assessment with per-path approved and primary flags and document availability, screening activities with the latest attempt and recorder, and placement approval. Actors that cannot be resolved render as an unresolved actor rather than exposing identifiers. [2]

### Documents

Documents are returned only when the caller holds `document.view`. Each entry has a purpose (`passport`, `placement_contract`, `readiness` or `supporting`) and an availability (`available`, `unavailable`, `inaccessible` for rejected documents, or `superseded`). A `downloadUrl` is set only for available documents and points at `/api/documents/:id/download`.

Criminal background evidence is served from document package artifact families when any exist: the original and its English translation appear as a parent and child pair with download URLs under `/api/document-packages/artifacts/:artifactId/file`, and raw criminal, background and police-clearance documents are filtered out to avoid duplicates. The UI groups children under their parent. [2] [6]

### Contract Comparison

The contract terms come from the application's completed, non-archived step whose key matches contract, placement or employment, reading snake_case fields (for example `salary_amount`, `pay_frequency`, `contract_start_date`, `contract_end_date`, `camp_contract_file`). The uploaded contract is linked through the step's document id. These are compared with the linked job order's offer terms on position title, amount, currency, frequency, start date and end date. `mismatch` is `null` when no field is present on both sides, and a missing value is shown as not recorded rather than as a difference. The comparison informs the reviewer and does not affect readiness. [2]

## Review Decisions

### Status Model

Decisions are appended to the review decision history of a work item with `review_domain` set to `sevis`. `reviewVersion` equals the number of recorded decisions.

| Decision | Allowed from | Resulting work-item status | Displayed status |
| --- | --- | --- | --- |
| `reviewed` | pending, needs_attention | completed | Reviewed |
| `needs_attention` | pending, completed | needs_attention | Needs attention |
| `reopen` | completed, needs_attention | pending | Not reviewed |

`needs_attention` requires a non-empty comment (maximum 2000 characters). A disallowed transition returns a conflict error. Reopening appends a decision; prior history is kept. Each decision, the work-item update and one governed audit record (`sevis_operational_review.decision`) commit in a single transaction. [1] [9]

### Concurrency and Idempotency

The request body carries `action`, optional `comment`, `expectedReviewVersion`, `expectedReadinessVersion` (an opaque token) and `expectedMaterialFingerprint` (64 hex characters). An `Idempotency-Key` header is required.

Inside the transaction the readiness projection row is locked first, then the review item, in that fixed order. The write is refused with a conflict when:

| Code | Condition |
| --- | --- |
| `SEVIS_REVIEW_MATERIAL_STALE` | Readiness is not fresh and ready, or the current material fingerprint differs from the one the client loaded |
| `SEVIS_REVIEW_REQUIRES_READY` | The locked projection is missing, unreadable, from an older configuration generation, not the version token the client loaded, or not `ready` |
| `SEVIS_REVIEW_VERSION_STALE` | `expectedReviewVersion` no longer equals the decision count |
| `IDEMPOTENCY_PAYLOAD_MISMATCH` | The key was already used with a different payload |

Replaying the same key with the same payload returns the existing result with `replayed` true and writes nothing. The client keeps one generated key per decision intent so a retry reuses it; the mutation retries once on network or 5xx failures, and any error refreshes the review query and the pool list, counts and review-queue queries. [1] [9] [10]

### Material Fingerprint

`versions.material` is a SHA-256 over canonicalized JSON of the review-relevant data: applicant identity and passport snapshot, identity comparison outcomes, passport documents, placements, contract terms and documents, the job order terms, eligibility assessment and approval, screening attempts, the readiness result with blockers and override contributions, and active overrides. `review.materialFresh` is true only when the latest decision recorded the same fingerprint. The footer shows "Review is stale" for a Reviewed applicant whose material changed, and the applicant must be reopened and reviewed again. [2] [3]

## Word Preview

`GET /api/documents/:id/word-preview` converts an application-owned Word document to a PDF for the reader. It requires `document.view` and entity scope, accepts only `.doc` and `.docx` files whose content type is missing, `application/octet-stream` or the matching Word type, and returns 400 otherwise. [11] [12]

- **Conversion.** LibreOffice (`soffice`, headless, `pdf:writer_pdf_Export`) runs in an isolated temporary directory with its own profile, a 45 second timeout, a 20 MB source limit and a 40 MB PDF limit; the output must begin with `%PDF-`. The directory is deleted afterward.
- **Sources.** Object storage paths and inline `data:` references are supported (base64 payloads over 28 MB return 413).
- **Failure.** Any conversion error returns 503 "Word preview is unavailable" and the UI offers the original download.
- **Response.** `application/pdf`, inline, `private, no-store`, `nosniff`. A `document_preview` PII access log entry is written.
- **Runtime.** The production image installs `libreoffice-writer` and `font-dejavu`. [13]

The download routes (`/api/documents/:id/download` and the per-version download) accept `?disposition=inline` and write PII access logs. [11]

## Eligibility Evidence

The SEVIS Review tab embeds a read-only eligibility panel fed by `GET /api/eligibility/applications/:id/assessment`, enabled only for users with `compliance.console.view`. Users without it see a message that detail needs compliance access, while readiness checks remain visible. A 503 from the endpoint renders as not available in this environment. The panel shows the sponsor decision, approver and timestamps, the primary and other approved paths with supporting signals and cited documents, and keeps unapproved alternatives in a collapsed list. An approval that is not currently valid is labelled "Approval needs re-verification" and "Previously approved" rather than current. The panel states that the SEVIS Ready decision is authoritative; it never changes the verdict. [14]

> **Why the assessment endpoint is not widened**
>
> The full assessment response includes raw fact snapshots and document identifiers, so it stays under the compliance permission instead of a SEVIS-only one. Serving SEVIS-only users would need a purpose-built redacted projection. [15]

## Readiness Gate Dependency

The Camp Counselor SEVIS gate can list `j1_camp_counselor.eligibility_path_confirmed` as a mandatory dependency without any program-bundle compliance check producing an outcome for it. That key is in the set of dependencies the SEVIS gate resolves live from the current Eligibility Engine determination, so it no longer stays `not_evaluated`. A live failure keeps the applicant blocked. [16]

## Worklist Integration

The Ready Pool page query filters in SQL by program, cycle, readiness state, blocker category, review status (`not_reviewed`, `reviewed`, `needs_attention`), recently-Ready preset (`last_24_hours`, `last_7_days`) and applicant name, ordered by application `updatedAt` descending with an id tiebreak. Review status for a row is the latest `sevis` review work item, mapped so that `pending` and `in_progress` read as not reviewed and `completed` reads as reviewed. Pool counts report not reviewed, reviewed and needs attention totals. Row summaries carry the operational cycle start and end dates. [17]

Batch selection uses `isReadyAndReviewed`, which requires the Ready gate check and a review status of `reviewed`. Selected applicants and the select-all action are filtered by it. An unselectable Ready row carries the hint "Complete the SEVIS operational review before selecting". This client check is best-known eligibility only; the server rechecks current review material at each batch write.

## Known Gotchas

- A Ready applicant whose readiness changes, or whose review material changes, cannot be reviewed from a stale workspace: the client must reload to obtain new `versions` values.
- Without `document.view` the `documents` array is empty and evidence links are absent even though the review data loads.
- Passport field values are null for users without PII unmask, which is expected and not a data gap.
- The passport comparison, contract comparison and operational evidence panels explain source records; none can turn a missing record into a pass.
- Word preview needs LibreOffice in the image; a local or custom image without it returns 503 and users must download the file.

## Sources

- PRs: BackOfficePrototype#2511, #2512, #2522, #2560, #2565, #2582, #2586, #2590, #2613, #2614 (workspace, evidence, reader, eligibility panel, contract comparison, gate fix).

[1]: https://github.com/Alliance-Strategies/BackOfficePrototype/blob/development/server/routes/sevisOperationalReview.ts "SEVIS operational review routes"
[2]: https://github.com/Alliance-Strategies/BackOfficePrototype/blob/development/server/services/sevisOperationalReviewService.ts "SEVIS operational review service"
[3]: https://github.com/Alliance-Strategies/BackOfficePrototype/blob/development/client/src/pages/sevis-pool/sevis-review-workspace.tsx "Review workspace component"
[4]: https://github.com/Alliance-Strategies/BackOfficePrototype/blob/development/client/src/pages/sevis-pool/use-sevis-review.ts "Review query and decision hooks"
[5]: https://github.com/Alliance-Strategies/BackOfficePrototype/blob/development/client/src/pages/sevis-pool/sevis-workspace-tabs.tsx "Workspace tabs"
[6]: https://github.com/Alliance-Strategies/BackOfficePrototype/blob/development/client/src/pages/sevis-pool/sevis-document-reader.tsx "Document reader"
[7]: https://github.com/Alliance-Strategies/BackOfficePrototype/blob/development/server/storage/sevisOperationalReviewStorage.ts "Review status and decision storage"
[8]: https://github.com/Alliance-Strategies/BackOfficePrototype/blob/development/server/services/sevisReviewBlueprint.ts "Review blueprint registry"
[9]: https://github.com/Alliance-Strategies/BackOfficePrototype/blob/development/server/storage/sevisOperationalReviewStorage.ts "appendSevisReviewDecision"
[10]: https://github.com/Alliance-Strategies/BackOfficePrototype/blob/development/server/services/sevisReadinessProjectionValidity.ts "Readiness version token and validity"
[11]: https://github.com/Alliance-Strategies/BackOfficePrototype/blob/development/server/routes/documents.fileServing.ts "Document file serving and word-preview route"
[12]: https://github.com/Alliance-Strategies/BackOfficePrototype/blob/development/server/services/wordDocumentPreview.ts "Word to PDF conversion"
[13]: https://github.com/Alliance-Strategies/BackOfficePrototype/pull/2613 "PR 2613: readiness gate, evidence reader and eligibility context"
[14]: https://github.com/Alliance-Strategies/BackOfficePrototype/blob/development/client/src/pages/sevis-pool/sevis-eligibility-evidence.tsx "Eligibility evidence panel"
[15]: https://github.com/Alliance-Strategies/BackOfficePrototype/pull/2613 "PR 2613 eligibility evidence access note"
[16]: https://github.com/Alliance-Strategies/BackOfficePrototype/pull/2613 "PR 2613 SEVIS gate eligibility-path dependency"
[17]: https://github.com/Alliance-Strategies/BackOfficePrototype/blob/development/server/storage/sevisPoolStorage.ts "SEVIS pool storage"
