# Recruitment workspace patterns

These companion examples address the eight patterns in `privy-career/docs/design-system-gaps.md`, using the workflow rules in `privy-careers-engineering/ENGINEERING.txt` (22 October 2026). They reuse Hermod tokens, DM Sans, custom selects, status badges, forms, modal focus behavior and responsive containers. All people, jobs, CV text and metrics are synthetic. No identity verification, file access, application, publication or message is sent to a service.

Open [Recruitment workspace](examples/recruitment.html), or the catalogue specimens: [Candidates and pipeline](index.html#recruitment-workspace), [Job openings](index.html#recruitment-jobs), [Talent pool](index.html#recruitment-talent). The full page is also available in Responsive preview. The catalogue and page use the same recruitment stylesheet and behavior module; keep their HTML specimens synchronized.

## Coverage of the requested gaps

| Priority | Pattern | Implemented specimen and interaction |
| --- | --- | --- |
| P0 | Hiring stage and candidate status | Text badges, conservative transition matrix, accepted/current offer gate, terminal outcomes and private rejection note. |
| P0 | Candidate table and filters | Team scope, role filter, stage filter, name/ID search, result count, name sort and four-row pagination. |
| P0 | Candidate profile drawer | Verified identity, application history, synthetic CV disclosure, separate consents, HR-only notes/rejection reason and candidate-visible status. |
| P1 | Application pipeline | Per-stage counts, candidate/job/team/date cards, keyboard Move stage and drag-to-review, optimistic revision conflict and reload. |
| P1 | Interview schedule and review | WIB schedule editor, round, assigned interviewer and separate HR/interviewer submissions and statuses. |
| P1 | Job opening and form | Team templates, department, location, arrangement, employment type, requirements and Draft/Published/Closed visibility. |
| P1 | Talent pool and saved pipeline | Separate membership and outreach stages, versioned candidate consent events, factual notes, internship interest and irreversible archive. |
| P2 | Recruitment dashboard | Cohort-based applications, in-progress, hires and conversion under one fixed period and permitted team scope. |

## Roles, team scope and privacy

The access selector is explicitly a local role simulation. **It is not authentication or authorization.** Fixture data is bundled in the browser. A production implementation must return only authorized records from server routes and repeat role, team and ownership checks for every read, file request and mutation.

- Super Admin can select all permitted teams or one team.
- The HR officer fixture is assigned to Engineering. It cannot select Design or use Design job templates.
- The interviewer fixture sees only interviews assigned to Morgan Lee and their associated candidates. It has no job management, talent membership, HR notes, private rejection reasons or DOB presentation.
- Switching role/team closes dialogs, clears private editor content and resets scope-dependent forms. Switching role also resets candidate query and stage/role filters to avoid carrying an unrelated search into the new access context. Search, pagination, pipeline, metrics, jobs and talent use the same scope.
- Candidate name and HR-visible DOB are read-only, server-owned identity values in the pattern. CV text is a synthetic disclosure; it never overwrites verified values. Real CV access requires a protected file route, not a public asset URL.
- Candidate-visible status is constructed independently. Private notes and rejection reasons are not concatenated into it. A saved internal note is not a sent message.

## Application stages and transitions

The example demonstrates a conservative ordered progression. The authoritative backend may define additional reviewed transitions; do not infer them from a generic Kanban library.

| Current stage | Permitted next steps in this example | Semantic presentation |
| --- | --- | --- |
| New | Screening, Rejected, Withdrawn | Info |
| Screening | Interview, Rejected, Withdrawn | Info |
| Interview | Offer, Rejected, Withdrawn | Info |
| Offer | Hired only with accepted current offer; Rejected; Withdrawn | Warning |
| Hired | None through ordinary HR actions | Success |
| Rejected | None through ordinary HR actions | Error, with explicit text |
| Withdrawn | Archived application; no reopening | Muted, with explicit text |

Rejected and Withdrawn are alternative outcomes, not mandatory columns between Interview and Offer. Rejected requires a private decision note in this specimen. Accepted-but-expired offers cannot move to Hired. The offer-condition control is a fixture simulator; real offer acceptance must originate from the authoritative candidate/backend event, never an HR assertion.

Move stage opens a confirmation dialog instead of applying immediately. Dragging a card also opens this same dialog; all operations are available through buttons and custom selects. Dropping into an invalid column does not bypass validation. Terminal cards cannot be moved. Revisions are captured when the dialog opens (or when dragging starts); a concurrent-update simulation increments the record revision. Confirm rejects an outdated revision. Load latest permits a fresh review but never retries the mutation automatically. Production updates need atomic version checks and ownership enforcement.

## Candidate list and profile

The candidate list shows verified display name/ID, role/team, stage, applied date and View profile. Search matches name/ID, and combines with role and stage. Sorting is announced with `aria-sort`; filters reset pagination. The result count and current page share a footer, with navigation at the opposite end. The table alone scrolls horizontally on narrow screens.

Profile order is identity and stage → verified identity → application history → CV → application/future consent → permitted private notes → candidate-visible status → available actions. Application consent and future-opportunity consent remain separate. The drawer is a named modal with focus containment, Escape/Close and focus restoration. It becomes a bottom sheet below 640px. Closing the drawer discards uncommitted textarea edits; Save internal note is the explicit local commit.

## Interviews and separate reviews

Dates in the editor are interpreted in **Asia/Jakarta / WIB (UTC+07:00)**. Cards include a human-readable date/time, timezone, round and named interviewer. No invitation is sent. Real products must define scheduling conflicts, availability and notification delivery before enabling these actions.

HR can schedule and submit the HR review. The assigned interviewer can submit only their own review. HR can inspect a submitted interviewer observation, but does not edit that separate record. Reviewer identity is derived from the simulated access context, not a user-editable form field. Reviews need at least ten non-whitespace characters. Pending/Submitted refers to the review, not application stage. Saving a review does not advance the candidate automatically.

## Job opening form

Templates and openings follow the selected permitted team scope. Fields cover title, department/team, location, work arrangement, employment type, requirements and visibility. A title and location are always required; Published also requires substantive requirements (minimum 20 characters in the demo). Draft and Closed do not accept applications. Saving merely updates local cards; publication in this fixture does not expose a public vacancy. Job templates do not widen access.

## Talent pool, consent and saved pipeline

Talent pool membership is a separate record from a job application. The sample records have no active application; job stages must not be reused as talent follow-up statuses. Display optional internship interest and team assignment separately.

| Future consent | Outreach behavior |
| --- | --- |
| Pending | No outreach permitted; no Contacted/Replied action. |
| Granted | Manual, fact-supported follow-up may proceed. |
| Declined | Archive membership. Ordinary HR cannot restore it. |
| Withdrawn | Archive membership. Ordinary HR cannot restore it. |

The candidate-event disclosure is a **fixture simulator**, not an HR permission override. Each simulated candidate response increments the consent version and record revision. Granting consent leaves the outreach stage unchanged. Decline/withdrawal immediately removes the membership from the active view; the Archived view has no reopening action.

The saved outreach sequence is Uncontacted → Contacted → Replied. Each advance requires a factual note (minimum ten non-whitespace characters in this demo) describing the interaction and date. Permission alone or sending a rejection does not count. No external communication is performed by recording a note. The dialog rechecks consent, team, archive and revision before applying. Real integrations need authenticated consent events, immutable consent history and transactional ownership/version checks.

## KPI definitions

All four cards share applications received **1–31 October 2026**, the same selected team and permitted role scope. Candidate list filters do not change this cohort; the caption states the distinction.

- Applications: count of authorized cohort applications.
- In progress: cohort applications whose current stage is not Hired, Rejected or Withdrawn.
- Hired: cohort applications currently Hired after acceptance of a current offer.
- Hire conversion: Hired / cohort applications, rounded to a whole percent; display an em dash for a zero denominator.

These are snapshot cohort metrics, not time-to-hire, hiring velocity or production SLA measures. No automatic ranking or scoring of applicants is implied.

## States, accessibility and responsive layout

The shared Data state selector covers Ready, Loading, Empty, Error, Permission-limited, Stale and Updated for all three specimens. Loading/Empty/Error/Permission-limited hide record surfaces and show explicit status text; they do not leave fixture rows visible as if current. Error/Loading can refresh; Stale keeps a labeled snapshot with mutations disabled until refresh. Role/team changes dismiss open dialogs. State changes invalidate in-flight simulated refreshes.

Use shared light/dark semantic tokens and textual status badges. Table and board overflow is contained to named focusable regions. At 640px, filters, job/talent cards and schedule forms become one column; KPI cards use two columns and the profile becomes a bottom sheet. All dropdowns use the shared searchable custom control. Focus indicators remain branded. Pipeline drag is optional because Move stage fully supports keyboard interaction.

## Validation and integration boundaries

Run `node scripts/recruitment-check.mjs`. It covers scope, role restrictions, table controls, candidate/private content separation, stage/offer/conflict guards, interview scheduling and review separation, job visibility, consent/follow-up/archive guards, state rendering, responsive bounds and scoped light/dark axe checks. This is UI fixture verification, not production access-control or privacy certification.

Backend services, real CV uploads/extraction, verified identity, candidate notifications, authentication, transactional revision enforcement, consent storage and retention remain integration responsibilities. The design system supplies the interaction contract and visual examples.
