# Applicant portal — agent knowledge

**Repository:** [`raisd-campus/applicant-portal`](https://github.com/raisd-campus/applicant-portal)
**SDD:** [SDD-04](https://github.com/raisd-campus/control-plane/blob/main/docs/sdd/04-applicant-portal.md)
**Campus agents:** [../AGENTS.md](https://github.com/raisd-campus/control-plane/blob/main/docs/ai/AGENTS.md)

## Status

A validated ApplicantPortalApi is implemented with shared package 0.6.2. Locally (no `VITE_PORTAL_API_URL`) it remains a session-only in-process mock. Hosted Demo builds call Portal API over HTTP (`POST /v1/auth/applicant/login`, `POST /v1/applicant/:method`). See [mock API and validation](https://github.com/raisd-campus/control-plane/blob/main/docs/ai/frontend/applicant-portal/mock-api.md). This portal is the **Admissions launch** surface (milestone 2): account, application, evidence, declarations, track status, offer, accept enrolment. Registry remains the decision owner.

## Agent rules

1. Implement against SDD-04 and the campus [AGENTS.md](https://github.com/raisd-campus/control-plane/blob/main/docs/ai/AGENTS.md). Do not invent a second admissions architecture.
2. Talk only to the Portal API. No direct CMS or database access.
3. Pin a released `@raisd-campus/design-system` version when application UI begins. Compose the shared `system/portal-shell` family with applicant-owned routes, navigation, account details and actions. Follow [component ownership and setup](https://github.com/raisd-campus/control-plane/blob/main/docs/ai/design/component-library.md); do not copy student portal components or shells.
4. Lesotho is the first acceptance pilot. Preserve current Malaysian sample content and use the captured [LUCT procedure](https://github.com/raisd-campus/control-plane/blob/main/docs/ai/backend/luct-online-registration.md) as a Cyberjaya research reference. Campus-specific Lesotho NMDS/LGCSE rules remain to be confirmed; Malaysia EMGS/NOC is not its default overlay.
5. When UI or API contracts appear, add feature docs under `docs/ai/frontend/applicant-portal/` and register them in [../MANIFEST.yaml](https://github.com/raisd-campus/control-plane/blob/main/docs/ai/MANIFEST.yaml).

## LUCT online registration baseline — 6 October 2026

Two campus procedures are captured for Applicant / Online Registration (and enrolled Add/Drop):

| Process | Summary | CAP |
|---|---|---|
| **A — Applicant online registration** | Public 4-step wizard (Academic → Documents → Personal → Submit) + `LUCT-MKT-003` admission form + EMGS health/visa pack | CAP-02, 03, 07, 55 (CY immigration CAP-50) |
| **B — Enrolled registration maintenance** | REG014/015 Add/Drop (+ change of programme and related Registry forms) | CAP-10, 51 |

Detail, SLAs (24h confirm / 2–3 day Registrar / 14-day completion), evidence matrix, and Lesotho substitution notes: [../backend/luct-online-registration.md](https://github.com/raisd-campus/control-plane/blob/main/docs/ai/backend/luct-online-registration.md). Page previews: [../materials/luct-online-registration/lkw-pages/](https://github.com/raisd-campus/control-plane/blob/main/docs/ai/materials/luct-online-registration/lkw-pages).

**FE gap matrix (all portals):** [../backend/luct-online-registration-fe-gap.md](https://github.com/raisd-campus/control-plane/blob/main/docs/ai/backend/luct-online-registration-fe-gap.md) · Pages: [online-registration-gap.html](https://github.com/raisd-campus/control-plane/blob/main/docs/diagrams/old-cms/online-registration-gap.html).

**Lesotho CMS vs Applicant (8 Oct 2026):** [../backend/lesotho-cms-vs-applicant-portal.md](https://github.com/raisd-campus/control-plane/blob/main/docs/ai/backend/lesotho-cms-vs-applicant-portal.md) · Pages: [cms-vs-applicant.html](https://github.com/raisd-campus/control-plane/blob/main/docs/diagrams/old-cms-lesotho/cms-vs-applicant.html). Captures Read.ai 7 Oct expectations, gaps after the campus + Eligibility→Offer merge, and the ordered Applicant backlog (hosted API parity, LGCSE/LSL, type matrices).

**Applicant merge · QR · fee SPs (9 Oct 2026):** [../backend/applicant-qr-fee-mechanisms.md](https://github.com/raisd-campus/control-plane/blob/main/docs/ai/backend/applicant-qr-fee-mechanisms.md) · Pages: [fee-payment-sps.html](https://github.com/raisd-campus/control-plane/blob/main/docs/diagrams/old-cms-lesotho/fee-payment-sps.html). Standup 8 Oct — merge analysis (`9e007f2`/`532d6f0`), QR automated+manual, fee/payment from CMS stored procedures (currency matrix · cases C1–C10).

## Submitted application UI preview — 1 October 2026

The working Applicant UI pins shared package 0.6.2. Without `VITE_PORTAL_API_URL` this is session-only mock behaviour. Hosted Demo builds wire the same `ApplicantPortalApi` over HTTP to Portal API. Saved serializable snapshots/conversations and revision/read/response state belong to the API adapter; original File objects and previews belong to its browser resource adapter. Message drafts remain portal-owned across navigation; mock refresh still resets the session. Existing draft and checklist flows remain independent.

- `/applications/:applicationId` shows **My Applications → Application Details**. Draft cards still open `/edit`; submitted cards open the detail route. Unavailable IDs show an unavailable state and a return action; submitted records cannot reopen the editor.
- The shared `PagePanelLayout` groups the review summary and five step accordions with its standard inset, matching Student's enclosing panel. Application Messages remains below the panel. The shared `SummaryCard` keeps its orange `warning` surface during review, including when a reviewer reply is required; **Action Required** is conveyed by its title and orange status badge; the list card uses the same warning tone. It contains reference, review stage/status, programme and dates, plus a shared application-progress `DataTable`. The table sits in `SummaryCard.footerContent`, retaining the divider above it, and contains four ordered **Application Progress** steps with **Step**, **Department**, **Status** and **Update** columns: Bursary payment proof, Quality Assurance documents/Eligibility release, applicant confirmation, and Registry Offer release. Eligibility release keeps the overall summary orange and Under review; final acceptance completes it. Operational Live routing remains an integration responsibility. The active review stage is **Under review**; the other row is **Pending review**. Payment proof shows its filename or **Not provided**; Document Check shows **Awaiting review**. Individual submitted files remain in the read-only step accordions. Phone uses the table's documented compact-card presentation.
- Review starts at **Payment Proof Under Review**. **Documents Under Review** follows development-only payment verification; applicant replies never advance review. A **Submitted Application** heading introduces the five step accordions. The default shared accordion primitives compose the existing raised, padded header pattern for Study Preferences, Personal Details, Academic Qualifications, Documents and Application Fee. All begin collapsed; multiple may open. Shared read-only `DetailField` rows preserve conditional sections and repeat records. Accordion titles use white 14px body text. File rows use `readOnly` with read-only `FileUploadField` tiles in the same value column as other fields; no replace/remove/edit actions are supplied. The submitted Application Fee accordion contains only Transfer Proof (Bank Reference Number and Payment Proof); bank transfer instructions remain in the editable application step. The editable fee summary always uses the orange warning variant, including before Student Type is selected.
- **Application Messages** reuses shared message rows, date dividers and the controlled composer. The Applicant adapter accepts text, attachments or both, with Student's five-file, 25 MiB per-file and 75 MiB total limits and error wording. Raster previews use JPEG/PNG/GIF/WebP; other files show metadata. Removed draft previews are released, and sent previews remain owned by the session.
- Submission and scenario initialization create no automatic messages. Explicit reviewer messages through Admin Actions start the conversation. An unanswered reviewer message gives **Action Required** priority over the review stage on both summary and list card. Actually visible reviewer rows advance the unread watermark; reading keeps the response requirement. Sending an applicant reply satisfies it and restores the current review-stage label without approving a document or advancing review. Reviewer controls exist only in the development tooling described below; there are no automatic replies.
- The shared unread `IssueCard` banner sits below the Application Details heading and focuses and scrolls to Application Messages. My Applications has no unread banner; each card still shows Action Required when a reviewer response is needed. Drafts and attachments remain isolated per application.

- The completed Documents Checklist footer uses the existing shared `PageActionBar`: secondary **Edit Answers**, followed by primary **Create Application**, with equal-width columns in the action group on phone and desktop, linking to `/applications/new`. Starting an application retains the checklist's session answers and prepared checkboxes.

Verification uses the full automated gate and responsive browser review. Automated Applicant tests are enabled; persistent storage remains outside scope. Live reviewer identity, authorization, notifications, file delivery and admissions decisions remain integration responsibilities.

## Related CAP focus

Admissions and identity capabilities in [SDD-11](https://github.com/raisd-campus/control-plane/blob/main/docs/sdd/11-capability-catalog.md) (see Applicant rows). CAP-53 is on the critical path for Live.

## Development Admin Actions — 5 October 2026

`/dev/admin-actions` and its sidebar footer link above Log out are development-only. The footer groups its actions with the same 8px vertical spacing (`space-y-2`) used by Student; the shared sidebar and buttons retain their existing geometry. The page uses the existing Applicant shell, shared Student scenario dropdown with its label above the field (matching Student's composition), two SummaryCards (payment and document verification), and existing shared chat presentation. No extra tabs, banners or dashboards. Production artifacts exclude the route, link, reviewer implementation and scenario fixtures; `check:production` is part of the full gate.

Exactly three scenarios switch the active applicant across the whole portal: **Fresh student application**, **Completed application — not submitted**, and **Submitted and approved application**. Each is initialized once per session. The latter two use complete validated synthetic snapshots and synthetic file resources; they represent neither genuine documents nor Live admissions. Switching retains saved drafts, checklist answers/prepared checks, messages and both sides' drafts/attachments independently. Queries include scenario and generation; leaving a scenario rejects in-flight writes without discarding its saved state. Refresh selects Fresh and recreates fixtures on demand.

The page reviews the latest submitted application, otherwise the latest draft or an empty state. Its reference appears once. Verification and messaging are disabled before submission. **Verify Payment** records payment verification and moves review to Documents Under Review while retaining unread/response requirements. **Verify Documents** requires verified payment and a valid immutable submitted snapshot, records verification and sets Approved. Repeated actions are disabled and rejected. No rejection controls: clarification and additional evidence go through messages.

Reviewer messages use the same conversation and limits as applicant replies, setting unread and Action Required. Admin viewing never acknowledges messages for the applicant. Approved takes precedence over response requirements and disables both composers while retaining unread state until the applicant visibly displays the messages. Individual completed checks use success badges, while the overall review summary stays warning until enrolment acceptance; approval alone does not provide accepted-enrolment context or provision Student records. The acceptance and next-login demo described below is separate from review. Development commands are not Applicant API methods or HTTP endpoints. See [mock operation details](https://github.com/raisd-campus/control-plane/blob/main/docs/ai/frontend/applicant-portal/mock-api.md).

## Acceptance and simulated Student handoff — 6 October 2026

Approved applications offer **Confirm and Accept Enrolment** in the SummaryCard footer. Its shared Popup shows the first preference's Intake, Faculty and Programme and explains next-sign-in Student Portal/module registration. Cancellation preserves Approved; confirmation stores guarded acceptance metadata, records **Enrolment Accepted**, then automatically logs out to the existing Sign in screen in development mock sessions. Clicking Sign in opens the existing new Student tab; it is never clicked automatically. The popup explicitly explains this sequence. Failed acceptance stays in the popup; failed logout retains accepted status with Retry Sign Out, without reaccepting. Pending confirmation and logout use existing component APIs. Approved/accepted conversations are read-only; submission/scenario initialization creates no automatic messages.

Both portals use independent copies of the canonical Design demo descriptor and a captured first-semester reference date. A development-only next-login exchange transfers accepted serializable data to a new Student tab and a new isolated mock account/enrolment. No data is stored persistently or put in URLs. Production and HTTP sessions exclude the simulation. See [mock operations and transport](https://github.com/raisd-campus/control-plane/blob/main/docs/ai/frontend/applicant-portal/mock-api.md#enrolment-acceptance-and-next-login-simulation--6-october-2026) and [accepted handoff](https://github.com/raisd-campus/control-plane/blob/main/docs/ai/backend/admissions-handoff.md#development-browser-handoff--6-october-2026). This is not live authentication, CMS provisioning or durable file delivery.

## Save before leaving — 6 October 2026

The Leave application popup uses released 0.6.2 with Discard Changes, Keep Editing and primary Save & Close in that visual/keyboard order. Save & Close accepts an incomplete draft through the existing mock API and continues the originally requested navigation only after success. Saving disables all dialog actions and dismissal; failed saves retain fields/files and show retryable feedback. Duplicate and stale session/unmounted completions cannot navigate. No new Applicant operation or persistence is added.

## Campus selection and issued letters — 7 October 2026

Current local mock workflow supersedes the earlier one-confirmation acceptance increment. Shared UI stays at 0.6.2. Hosted Applicant Demo HTTP authentication remains intact. The incoming 9 October backend documentation reports ten RPC methods, including Eligibility confirmation and final acceptance, with automatic Demo Offer release. This frontend checkout still explicitly gates hosted confirmation/acceptance; enabling and verifying that frontend parity is separate work. Staff-owned Offer release, durable authorized Student file delivery and Live CMS writes remain pending. This local increment does not alter Portal API service code or database records.

Step 2 requires Campus (Lesotho, Cyberjaya, Botswana, Eswatini, Sierra Leone, Cambodia, Uganda, Namibia). Alumni additionally requires a trimmed Previous Student ID. A campus change clears dependent preferences. Drafts remain saveable when incomplete. English Language Results are optional for every sample programme; populated rows must be valid. All eight campuses expose the existing Design curriculum as **demo content**, not verified campus offerings, fees or admissions policy. Selected campus metadata and frozen intake reference are retained through acceptance and Student bootstrap.

Payment Proof is reviewed by **Bursary**; Document Check by **Quality Assurance**. QA Verify Documents validates the submitted snapshot and atomically releases a generated **Eligibility Letter** PDF. The applicant's **Confirm and Continue Enrolment** popup shows the intake, faculty and programme and preview/download actions. Confirmation records intent without logout or Student provisioning. Registry's **Release Offer Letter** is enabled only afterwards. The final **Accept Enrolment** popup previews/downloads that Offer Letter. Only successful final acceptance triggers automatic logout; the applicant clicks Sign in to open the existing Student tab. Cancel changes nothing. Saving disables confirmation and dismissal; failure retains data and allows retry. Logout failure can be retried independently of acceptance. All writes retain ownership, revision, session and request-id guards.

Generated PDFs are prominently labelled DEMO, not official university letters, and contain the application reference, applicant, campus, intake, faculty, programme and release date. Original Unicode names remain canonical; the demo PDF's standard font uses a safe ASCII transliteration with replacement for unsupported glyphs. QA/Registry letters, releases and confirmations live outside the immutable submitted snapshot and are distinct from applicant-uploaded eligibility evidence. Release creates no automatic conversation or unread messages. Approved/accepted conversations remain read-only. The approved scenario now awaits Eligibility confirmation; Registry release and final acceptance remain user actions.

Admissions handoff and browser envelope **v2** include the selected campus and both letters/confirmations. The producer/consumer validate sequence and bounded PDF resources. V1 remains readable for legacy records without fabricated letters. Local generated PDF bytes travel only in the one-time validated development postMessage exchange, never URLs or persistent browser storage. Existing origin/window/nonce/replay checks and session cleanup remain. Student Documents shows issued Offer Letter first, Eligibility Letter second, with working preview/download for these resources. Other legacy documents retain their existing delivery capabilities. Payment proof remains outside Profile documents.

The selected-campus bootstrap clones only sample academic/campus configuration and remaps its IDs and local calendar windows. It creates an isolated identity/enrolment, inherits no prior results, registrations, payments or visa approvals, and preserves existing credit/fee/clash/international restrictions. Non-Cyberjaya fees and curriculum remain explicit prototype samples, not campus policy. Refresh disposes mock state and temporary resources. Durable evidence delivery, hosted two-letter methods and real CMS provisioning remain future work.

Mock operations: `confirmApplicationEligibility` (applicant), `verify-documents` with Eligibility generation (development QA command), `release-offer` (development Registry command), and guarded `acceptApplicationEnrolment` after Offer release. No new HTTP routes are exposed. Local DTOs may include optional `issuedLetter` PDF resources; hosted Profile document responses remain metadata-only.

## Review progress and letter actions — 8 October 2026

Document verification and Eligibility Letter release do not complete enrolment. The review SummaryCard stays orange (`warning`) with an **Under review** badge through Eligibility confirmation, Registry preparation and Offer release. Stage-specific headings retain the current task; only **Enrolment Accepted** uses the green completed summary.

The section is titled **Application Progress**, with **Step** as the first column and accessible table name “Application progress.” The review table has four ordered rows: Payment Proof (Bursary), Document Check (Quality Assurance), Confirm and Proceed with Enrolment (Applicant), and Prepare and Release Offer Letter (Registry). Each row derives its own Status and Update from verification/letter metadata. Document verification shows the released Eligibility filename; applicant confirmation records its date; Registry changes from awaiting applicant confirmation to preparing the Offer, then shows the released Offer filename. A completed row may be green while overall enrolment remains under review. This is presentation of existing guarded workflow state, with no API/schema changes.

Both confirmation popups and Student Documents use two equal-width letter actions with icons and spacing above them. **Preview Letter** opens a temporary PDF URL in a separate tab using the browser's PDF handling; **Download Letter** downloads the same PDF with its issued filename. There is no embedded preview popup or extra disclaimer below a viewer. The generated PDF itself retains its DEMO marking. Temporary resources retain their existing ownership/cleanup behavior. Hosted delivery support and physical-browser PDF handling remain distinct from the local mock checks.

## Student evidence transport — 8 October 2026

DEV next-login delivery uses bounded envelope v3, preserving admissions snapshot v2 and v1/v2 receiver compatibility. Only available owned academic/identity/admissions evidence is delivered individually with MIME/name/length/hash checks; payment evidence is excluded. Missing bytes remain unavailable in Student. URLs carry only a nonce; no persistent storage or new hosted integration is added. See [Student workflows](https://github.com/raisd-campus/control-plane/blob/main/docs/ai/frontend/student-portal/student-workflows.md).

## Applicant–Student synchronization — 12 October 2026

The 12 October local preparation supersedes earlier statements that original HTTP file delivery and applicant-specific continuation are wholly unimplemented. [Demo admissions resources](https://github.com/raisd-campus/control-plane/blob/main/docs/ai/backend/admissions-resources.md) defines the separate Demo artifact adapter, one-time authenticated continuation, capability gating, ownership and local verification. It is **not deployed**; Live storage/CMS provisioning and hosted migration remain pending. Shared UI remains 0.6.2; admissions v2, DEV transport v3 and Student schema 4 are preserved. The existing Bursary shortcut remains On hold.
