Imported from MuhammadRaedSiddiqui/pcao-f-practice-exam (
AGENTS.md). Install upstream withnpx skills add MuhammadRaedSiddiqui/pcao-f-practice-exam. Copyright stays with the author.
Repository Guidelines
Project Structure & Module Organization
This app uses React, TypeScript, and Vite with optional Supabase accounts for completed-report backup. src/App.tsx coordinates Start, Exam, Review and Results; src/ui/ResultsPage.tsx renders completed snapshot reports; src/styles.css contains responsive styles. Keep validation, attempt, and Results summary logic in src/domain/, reusable controls and Markdown rendering in src/ui/, and runtime data in public/question-bank.json (Advanced) and public/easy-question-bank.json (Intermediate). Unit tests are in tests/*.test.ts(x), browser tests in tests/browser/, and synthetic fixtures in tests/fixtures/. Read PROJECT_IMPLEMENTATION_STATUS.md for the unified implementation and current status. Read IMPLEMENTATION_SPEC.md before changing the flow or schema.
Current Local Status
Authentication deferred during exams — 2026-10-16: Exam/Review/Submit now show account status only, hiding signup/sign-in, Settings and recovery actions. Guest guidance explains authentication is available after leaving/submitting. Auth forms unmount using their existing cancellation cleanup; the account hook/session and pending recovery context stay active. Leaving through the existing confirmation or submitting restores account controls, with pending recovery resuming outside Exam. TypeScript/build pass; all 214 unit tests across 23 files pass across the initial 203-test run and unchanged 11-test database retry after PGlite setup timeouts. Real in-app Chromium native-control checks cover guest start/reload/answer/flag/resume/review/submission, synthetic sign-in, signed-in status/reload, and recovery deferral/restoration. Five responsive guest layouts and narrow/desktop signed-in layouts were inspected. No Playwright/DOM browser automation, hosted account mutations, emails, deployment, commit or push. See EXAM_AUTH_RESTRICTION_VERIFICATION.md. QA tab closed and viewport reset; temporary server ports no longer respond.
Settings and history routes — 2026-10-16: Added /settings and /history with React Router, real header links, direct-link/reload support, protected Settings guidance, sign-out replacement, validated saved-attempt restoration and shared active-exam confirmation for Back/Forward. Home retains its history preview. Vercel rewrite configuration is ready for deployment; History has separate analytics/focus handling. Narrow minimum-width clipping was corrected. Final TypeScript/build and all 208 unit tests across 21 files pass. Real Chromium native-control checks used the rebuilt guest app and a local synthetic HTTP auth boundary for signed-in Settings; no Playwright/DOM automation or hosted account mutation occurred. See SETTINGS_HISTORY_ROUTES_VERIFICATION.md. QA servers/tabs stopped and viewport reset; no deployment, emails, hosted changes, commit or push.
Signup automatic backup — 2026-10-16: Existing guest-report migration was already independent of Settings; the owner clarified that future new exams should also back up by default for new accounts. Signup now saves this enabled preference and prevents the initial preference read from overriding it; sign-in preserves existing opt-outs. Real in-app Chromium native-control tests against configured Supabase pass for guest-report migration, empty signup without placeholders, automatic future-exam backups in both cases, reload persistence and saved opt-out after sign-in. No Playwright/DOM automation was used. TypeScript/build pass; all 204 unit tests across 20 files pass across the initial run and a focused 11-test retry with a longer PGlite setup timeout. Both disposable accounts and their three QA report backups were deleted after final owner confirmation; both deleted credentials were rejected. See SIGNUP_AUTOMATIC_BACKUP_VERIFICATION.md. No deployment, emails, hosted configuration/schema changes, commit or push.
Observed UI bugs fixed — 2026-10-16: RBQA-001 dialogs now use fixed viewport anchoring, root scroll isolation and contained body scrolling; heading/Close/actions remain visible after resizing from scrolled Results. RBQA-002 zero-unanswered confirmation uses neutral complete-selection copy; positive counts retain the warning and singular/plural wording. TypeScript and build pass. The initial full-worker unit run hit two PGlite setup timeouts; an unchanged two-worker rerun passed all 204 tests across 20 files. Real in-app Chromium accessibility/mouse/keyboard checks, without Playwright/DOM automation, verified component scroll/resize/focus/drawer/enlarged-text/copy cases and the rebuilt app's Results-selector reproduction across five widths/layouts. The Chrome extension was unavailable. Reviewed bank hashes are unchanged. Deletion-timeout and obsolete-suite source findings remain open. See OBSERVED_BUG_FIXES_2026-10-16.md and test-results/observed-bug-fixes/. No hosted/account changes, emails, deployment, commit or push. Temporary QA servers stopped and viewport overrides reset.
Real Chrome QA and analysis — 2026-10-16: At the owner's request, current-source TypeScript, all 204 unit tests across 20 files and production build pass. Real Chrome accessibility/mouse/keyboard automation, without Playwright/DOM automation, verified mixed Intermediate grading (17/1000), a complete 60-answer Advanced run (1000/1000), reload/resume, review, PDF downloads, real disposable-account signup/migration/backup, fresh-origin cloud recovery, session restoration, settings, cross-tab submission and password-protected account deletion. The disposable account was deleted after explicit final confirmation and subsequent login was rejected. Controlled local HTTP bank failures/retries also pass. Responsive testing found selector heading/Close clipping after viewport shrink at retained scroll positions; zero-unanswered copy is misleading. Source review retains unbounded deletion-wait and obsolete browser-test concerns. Natural two-hour expiry, real mailbox/password reset, touch, controlled enlarged text, other browsers and broader fault cases remain unverified. See REAL_BROWSER_QA_2026-10-16.md and test-results/visual-qa-20261016/. No app source/bank edits, emails, hosted configuration change, deployment, commit or push occurred. QA servers were stopped and viewport overrides reset.
Account backup origin correction — applied after approval, 2026-10-16: The owner approved the exact shared allowlist change. ARCHIVE_ALLOWED_ORIGINS now includes https://wager-geranium-crafter.ngrok-free.dev plus the existing Vercel/six local origins. All 11 HTTP origin/auth checks pass; all three ngrok preflights changed from 403 to 204, production/local still work, anonymous requests remain 401 and an unrelated origin remains 403. An authenticated HTTP-only real-bank backup/receipt check using the ngrok Origin header also passes, and its disposable account/report were deleted. The latest QA origin remains unconfirmed; this fixes the proven ngrok rejection, without claiming a replay of the QA queue. No browser tests, emails, source patch, unit/type/build, frontend deployment, commit or push occurred. See ACCOUNT_BACKUP_FAILURE_STATUS.md, test-results/backup-origin-after.json and test-results/backup-ngrok-authenticated-after.json.
Account backup investigation — 2026-10-15: Authenticated HTTP-only diagnostics successfully created a disposable account, read preferences/history, backed up a real Intermediate report and read its receipt; the temporary account/report were deleted. The earlier ngrok origin receives 403 from all three archive function preflights while production/local and anonymous protections behave as expected. That tunnel is currently offline and the latest QA origin is not confirmed. An exact-origin addition retaining all seven recorded origins is prepared; automatic approval review rejected the shared trust-setting write pending explicit authorization, so it was not applied and no workaround was used. All 11 baseline HTTP expectations pass, including expected ngrok denials. No browser tests, source patch, unit/type/build, hosted change, deployment, commit or push occurred. See ACCOUNT_BACKUP_FAILURE_STATUS.md.
Recovery enabled in normal accounts — 2026-10-15: At the owner’s request, password recovery now defaults on when Supabase accounts are configured; VITE_PASSWORD_RECOVERY_ENABLED=false is an explicit opt-out. Local configuration and .env.example use true. The recovery test configuration no longer forces enablement. Final verification passes TypeScript, 46 focused unit checks, all seven synthetic Chromium recovery cases in one run and the production build (existing size warnings remain). Reload/resume, safe invalid-link resend and session-bound cleanup are included. The frontend still needs deployment; remove/change any old Vercel false override when deploying. Live Gmail delivery/reset/login remains unverified. No live emails, account/password/report mutations, hosted changes, deployment or push occurred. See PASSWORD_RECOVERY_RESUME_STATUS.md and test-results/recovery-enable-* evidence.
Password recovery verification — 2026-10-15: Following explicit owner approval, TypeScript, all 42 focused unit checks, all seven synthetic Chromium recovery cases and the production build pass on current source. Browser checks cover Cancel/Close/Escape with reload, successful password-update cleanup, safe expired-link resend, unauthenticated recovery flags and sign-out cleanup. Recovery remains gated off in local/production configuration; a controlled real-mailbox delivery/reset/login check and frontend rollout remain pending. Existing build size warnings are nonblocking. No emails, real account/password/report mutations, hosted settings, deployment, new commit or push occurred. See PASSWORD_RECOVERY_RESUME_STATUS.md and test-results/recovery-resume-{typecheck,unit-tests,browser,build}.txt.
Password recovery resumed — 2026-10-15: Owner screenshots show Gmail SMTP configuration and the production Site URL/allowed redirect. Delivery and SMTP save are not independently verified. Local recovery now has account/session-bound pending context for reload, a Finish password reset action after dismissal, safe invalid-link errors/resend and callback URL cleanup. The feature flag remains off pending validation. All 42 focused helper/error/cancellation unit checks pass; later static refinements were not rerun. Automatic approval review rejected the TypeScript check under the retained no-typecheck restriction; no workaround was used. Seven synthetic recovery browser cases are prepared but unrun. No browser/build, emails, hosted/account mutations, deployment, new commit or push occurred. See PASSWORD_RECOVERY_RESUME_STATUS.md.
PostHog recheck — 2026-10-15: Current working-tree TypeScript, 26 focused analytics unit tests and production build pass. Five of six synthetic Chromium analytics cases passed initially; the initial guest pageview exceeded its five-second polling limit, then the unchanged case passed on a focused rerun. All six scenarios pass across two runs, with intermittent startup/delivery test timing unresolved. Local analytics is enabled with environment=development. A read-only inspection of the production Vercel entry /assets/index-b0EMpgL0.js found no PostHog tracking; the current integration still needs production configuration and deployment. No live analytics sends, real account/report mutations, hosted changes, new commit or push occurred. See POSTHOG_VERIFICATION.md and test-results/posthog-review-* evidence.
BUG-004 Production origin correction — applied after approval, 2026-10-15: The owner explicitly approved the shared origin change. Supabase ARCHIVE_ALLOWED_ORIGINS now includes https://pcao-f-practice-exam-one.vercel.app alongside the six recorded localhost/127.0.0.1 development origins. All eight targeted HTTP checks pass: three production preflights return 204 with the exact origin; three anonymous requests remain 401; an unrelated origin remains 403; 127.0.0.1:5173 remains allowed. The confirmed production preflight blocker is resolved without frontend rebuild or Edge Function deployment. Actual password-authenticated deletion was not exercised and qa-pcaof-20261014@example.com was not deleted; no authenticated browser/app session or QA password was available. No manual deletion workflow or frontend patch was added. See BUG_004_ACCOUNT_DELETION_STATUS.md and test-results/bug-004-origin-fix-verification.json. No unit suite, typecheck, build, browser regression suite, account mutations, commit or push occurred.
BUG-004 Production account deletion — origin blocker confirmed, 2026-10-15: A read-only OPTIONS request from the owner-supplied https://pcao-f-practice-exam-one.vercel.app origin to the deployed delete-account function returned 403/auth: This app origin is not allowed, with no allowed-origin response header. The service is reachable but rejects the production site before password verification. The repository maps the resulting fetch failure to a generic history error. Adding the exact production origin to the shared ARCHIVE_ALLOWED_ORIGINS setting, retaining the six recorded development origins, is prepared but not applied: automatic approval review rejected the hosted trust-setting change pending explicit authorization. No workaround was attempted. The owner declined a manual deletion/support workflow; no support link or frontend patch was added. QA account qa-pcaof-20261014@example.com remains unremoved because no connected authenticated dashboard/app session was available. See BUG_004_ACCOUNT_DELETION_STATUS.md and test-results/bug-004-production-origin.json. No account mutations, tests, typecheck, build, browser regression suite, hosted configuration changes, commit or push occurred.
Password recovery postponed — 2026-10-15: At the owner’s request, recovery is disabled unless VITE_PASSWORD_RECOVERY_ENABLED=true; missing/false defaults off. Forgot password is hidden, and sign-in/signup display a clear recovery-unavailable reminder to keep the password safe. Recovery request/password-update handlers reject calls while disabled, and recovery events do not open the new-password dialog. Signup/sign-in/history remain available. The implementation and safe error diagnostics are retained for later SMTP/callback setup and authorized verification. This is a frontend feature gate; hosted Supabase Auth APIs/settings are unchanged. .env.example and setup/spec/README document the flag and later enablement. The owner reports Vercel deployment; this local change still requires a rebuild/redeploy to reach that site. No tests, typecheck, build, browser checks, email sends, hosted settings, deployment, commit or push occurred.
BUG-005 Password recovery — SMTP setup required, local diagnostics improved, 2026-10-15: The owner confirmed custom SMTP is not enabled. Supabase’s default sender restricts delivery to project-team addresses, explaining failure for ordinary recipients; the exact hosted recover response remains unavailable and no live delivery test was performed. Recovery now maps safe provider codes/HTTP status to actionable configuration/rate-limit/network/service guidance instead of one generic error. Raw provider messages and account-existence codes are excluded. Missing client configuration fails explicitly; accepted-request copy is conditional and does not promise delivery. Added unrun unit cases and SMTP/redirect setup steps in SUPABASE_SETUP.md. No tests, typecheck, build or browser checks were attempted under the retained no-confirmation-checks instruction. Automatic approval review rejected probing saved admin credentials; no alternate access was attempted. Owner-supplied non-secret SMTP status supplied the configuration evidence. No hosted settings, emails, account/report mutations, deployment, commit or push occurred. Public email delivery and the earlier recovery resume/reload/callback issues remain unresolved. See BUG_005_RECOVERY_STATUS.md.
BUG-002 Browser BACK — partially confirmed locally, 2026-10-15: The reported ngrok URL and existing localhost:5173 server timed out, so hosted behavior is unverified. Four isolated Chromium guest sessions ran on current source using a separate localhost:4194 server, which was stopped afterward. New/resumed active-exam BACK emitted native beforeunload warnings in both sessions: dismissing retained Q2; accepting reached an explicitly supplied blank predecessor. The custom in-app Leave exam dialog did not appear. Both completed Results sessions left without a warning, as no active attempt remained. FORWARD returned Home in all four sessions; saved ID, answers, flags, position, start/deadline and submitted result metadata were unchanged. Missing SPA history is confirmed by source/CDP history; a silent native-warning bypass is not reproduced in current local Chromium. See BUG_002_BROWSER_BACK_VERIFICATION.md and test-results/bug-002-{verification,hosted-attempt}.json. One cancellation screenshot was inspected. No implementation edits, typecheck, unit suite, build, account/cloud mutations, deployment, commit or push occurred. This targeted navigation check does not verify BUG-001’s pending fix or establish a broader regression pass.
BUG-001 Start dialog click-through — local fix, verification pending, 2026-10-15: Successful new/replacement starts arm a click capture guard in App. Multi-click continuation events are cancelled before activating an answer label in the exposed Exam view; the first fresh single click or keyboard activation releases the guard without a timer. Leaving Exam resets it. Existing start locking, snapshots, answers and deadlines are otherwise unchanged. Added 18 targeted pointer/persistence/replacement/keyboard/touch regression cases in tests/browser/start-click-through.spec.ts, including both difficulties and seven layouts. These tests have not run. Automatic approval review rejected npm run typecheck because the user’s retained no-confirmation/no-browser-tests instruction remains in effect; it did not treat fix it as authorization. The command did not execute and no workaround was attempted. Explicit approval is needed for TypeScript/focused browser checks. Source was reviewed only; no post-fix runtime verification, build, screenshots, deployment, commit or push occurred. See BUG_001_START_CLICK_THROUGH_VERIFICATION.md. Earlier reproduction evidence remains valid historical evidence, not proof of the fix.
BUG-001 Start dialog click-through — confirmed, 2026-10-15: Six targeted isolated guest Chromium trials ran against the reported ngrok URL at 1919 × 998. Intermediate double-click reproduced 2/2: the second pointer click reached Q1 option B after the dialog closed, saved answers Q1=[B] at revision 1 and survived reload. Each created exactly one attempt. Advanced double-click reproduced 0/2 at this viewport because the second click hit question text; both single-click controls opened empty at revision 0. The supplied screenshot identifies Intermediate. Recorded pointer events establish the click-through cause; the existing programmatic repeated-button-click test does not exercise this hit-testing transition. High-severity unintended persisted input is confirmed; submitted-report persistence was inspected in code, not re-exercised. See BUG_001_START_CLICK_THROUGH_VERIFICATION.md, test-results/bug-001-reproduction.json and screenshots/bug-001-*.png. The Intermediate screenshot was inspected. No application code changes, unit suite, typecheck, build, broad browser run, account/cloud mutations, deployment, commit or push occurred. This bug remains unresolved.
PostHog analytics — implemented locally, 2026-10-15: The configured browser SDK now records virtual Start/Exam/Results/Account settings visits, accepted signup/explicit login/restored signed-in activity, logout, saved exam starts/resumes/completions (including expiry), review opening, PDF-download initiation and confirmed report backups. Anonymous/account identity transitions and lazy-load buffering preserve event attribution; token refresh does not count as another login. Estimated active time counts visible, focused use with a five-minute input idle cutoff and excludes suspension gaps. Events contain allowlisted metadata; DOM autocapture, replay, heatmaps, automatic exceptions/performance and console recording are disabled. SDK public project tokens needed for ingestion are retained while auth tokens, private content and URL query/fragment data are scrubbed, including initial person updates. Analytics failures do not gate existing app operations. TypeScript, all 162 unit tests across 18 files, production build and all six isolated Chromium analytics cases pass. A real development-only smoke check against the configured US PostHog project received HTTP 200 for pageview and engagement events; no Supabase account/report mutations occurred. Source/built banks and approved Markdown hashes are unchanged. See POSTHOG_SETUP.md, test-results/posthog-{typecheck,unit-tests,build,browser}.txt, test-results/posthog-hosted-browser.json and test-results/posthog-content-integrity.json. Production build retains nonblocking size warnings (main about 783 kB, lazy SDK about 306 kB, PDF about 1,834 kB). Dashboard configuration, production analytics environment/deployment and broader guest/account browser regression runs are not established by this focused verification. No SQL/functions, commit, push or frontend deployment occurred.
Account deletion/password recovery review — 2026-10-15: Static review confirms token-derived owner/current-password deletion safeguards, cascading database records and the normal owner-isolated local cleanup path. Historical hosted evidence records protected deletion, temporary-account removal and rejected tokens; no new runtime pass was run. Recovery uses the Supabase two-stage request/update flow and cancellable auth transport, but review found missing resume after dialog dismissal, lost recovery context on reload and unsurfaced invalid/expired-link errors. Account deletion also has no explicit timeout while its dialog disables dismissal. These findings remain unresolved. Hosted SMTP, recovery redirects and the current app origin were not queried; recorded localhost-only configuration does not establish ngrok support. See ACCOUNT_RECOVERY_DELETION_REVIEW.md. No feature edits, tests, typecheck, build, browser checks, email sends, live account mutations, deployment, commit or push occurred.
Intermediate Exam title — fix 5 of 5, 2026-10-15: The Exam bar now uses the existing resultTitle presentation helper shared with Results/PDF. Attempts saved as Intermediate display Independent PCAO-F Practice — Intermediate instead of the frozen reviewed bank’s original — Easy title. Advanced, custom and legacy titles retain their saved text under the helper’s existing rules. No bank, snapshot, persisted report, grading or timer behavior changed. All five requested fixes are now implemented locally. Per the user’s instruction, work stops after this fix. No tests, typecheck, build, browser checks, deployment, commit or push were run for fixes 1–5; prior verification totals remain historical. Existing test expectations for the revised UI have not been updated or run.
Browser-session authentication — fix 4 of 5, 2026-10-15: Unchecked Keep me signed in sessions now store Supabase tokens in sessionStorage rather than only memory, allowing same-tab reload recovery. Checked sessions continue using localStorage. The tab’s chosen mode is retained in sessionStorage so another tab changing the device preference does not override it on reload. Mode changes move the existing token after clearing old copies; refreshed tokens use the chosen store. Session removal/sign-out clears both stores and the memory fallback independently. The cancellation session-write fence remains unchanged. Unchecked sign-in retains a memory fallback if sessionStorage is blocked; that fallback does not survive reload. Browser session restoration follows the browser’s own sessionStorage behavior. Fixes 1–4 are complete locally; the Intermediate Exam title remains pending as fix 5. Per the user’s instruction, work stops after this fix. No tests, typecheck, build, browser checks, deployment, commit or push were run; previous verification totals are historical. No SQL or function changes were needed.
Shared Source courses — fix 3 of 5, 2026-10-15: Removed the Intermediate/Advanced toggle, level-specific heading, drawer difficulty props/state and unused heading style. The drawer presents one course list with a note that both difficulties use these courses. It derives course title/URL pairs from a ready validated bank, preferring Advanced when both are ready and using Intermediate when Advanced is unavailable. Loading/error/unavailable states remain; retry requests both banks independently. Existing dialog focus/dismissal, safe links and scrolling are retained. Exam difficulty selection, reviewed bank files and saved snapshots are unchanged. Fixes 1–3 are complete locally; browser-session auth persistence and the Intermediate Exam title remain pending as fixes 4–5. Per the user’s instruction, work stops after this fix. No tests, typecheck, build, browser checks, deployment, commit or push were run; previous verification totals are historical. Existing drawer browser-test expectations have not been revised or run.
History actions simplified — fix 2 of 5, 2026-10-15: Removed the Import device history dialog/action and account-history JSON export, including selection state, download/progress/cancellation code and the public bulk-import/export hook API. Automatic signup migration and automatic account-history loading remain in place; empty guest history creates no report rows. Individual report backup/resume/retry controls use the same private queue helper, retaining ownership, validation and synchronization safeguards. Export is available only for individual Results reports as PDF. Settings, empty-history and account-deletion copy reflect this behavior. Fixes 1–2 are complete locally; source-drawer simplification, browser-session auth persistence and the Intermediate Exam title remain pending as fixes 3–5. Per the user’s instruction, work stops after this fix. No tests, typecheck, build, browser checks, deployment, commit or push were run. Previous verification records are historical; existing browser test cases for the removed features have not been revised or run. No database or function changes were needed.
Account settings page — fix 1 of 5, 2026-10-15: Account settings now have a dedicated internal page reached through the signed-in header Account settings button. Backup preferences, trusted-device copies, sign-out, account deletion and the existing import/JSON-export controls are moved there. Home retains account reports and backup notices; Results retains report-specific actions. Trusted-device preference is held above the page components, applies when opening Home reports and resets on account changes. Navigation from an unfinished Exam reuses the leave confirmation and continuing-deadline safeguards; settings heading/back navigation have focus targets, and sign-out returns to Home. Analytics recognizes the settings view without exam difficulty. This is fix 1 only; removing import/account JSON export, simplifying the course drawer, browser-session auth persistence and the Intermediate exam-header title remain pending as fixes 2–5. The user explicitly requested stopping after each fix and no confirmation/browser tests. No tests, typecheck, build, browser checks, deployment, commit or push were run for this change. Prior verification totals are historical, not evidence for this new page.
BUG-AUTH-001 / BUG-AUTH-002 — fixed, 2026-10-14: Signup now checks ASCII mailbox syntax, local/total lengths, fully qualified domain labels and alphabetic/IDNA suffixes before sending the request. Migration 202610140002_registration_email.sql enforces the same rule on new/changed auth.users emails, including direct Auth API signup; it is applied to the linked hosted project. Existing accounts and unchanged legacy emails are preserved. This is format/domain-shape validation, not DNS/deliverability or ownership verification; email confirmation remains disabled. Auth dialogs keep Cancel, Close and Escape available while submitting. A scoped AbortController transport cancels requests/body reads, rejects late responses and fences cancelled session writes/events; signup/signin/recovery/password-update requests time out after 30 seconds with an actionable retry/sign-in message. Timeout retains entered fields; cancelling clears the password and restores opener focus. Narrow device-report rows no longer wrap into an overflowing column. TypeScript, all 136 unit tests across 16 files, production build and all 31 account Chromium cases pass. Pending signup cancellation works at all six layouts with all three dismissal methods, no late session/ownership migration and successful subsequent sign-in; timeout/retry and ordinary signup/backup/recovery regressions pass. Mobile/desktop pending-dialog screenshots were inspected. Direct hosted malformed-signup rejection and valid-signup health checks pass; hosted browser migration/empty signup also pass, with temporary accounts removed. The short-domain direct API rejection currently returns HTTP 500/code 23514; the app detects the same format locally and shows a clear message without issuing that request. See AUTH_FIXES_VERIFICATION.md and test-results/auth-fixes-{typecheck,unit-tests,build,account-browser}.txt plus hosted/content JSON evidence. Reviewed source/built hashes are unchanged. Development server runs at http://127.0.0.1:5173. No new full guest-suite run, frontend production deployment, commit or push occurred; only the new SQL migration was applied.
Automatic signup migration and header authentication — 2026-10-14: New email/password signup automatically claims and queues valid, completed guest browser reports without an import prompt. Device report bytes remain unchanged; empty browser history creates no account report rows. Unfinished, private/owned, paused/deleted and unreadable records are excluded. Signup prepares durable ownership/queue bookkeeping before starting synchronization, shows pending jobs immediately, and retries recover after reopening. Sign in/Create account and authentication/recovery dialogs now live in the right side of the shared responsive header on Start, Exam and Results; signed-in report management stays on Start/Results. Verification passes TypeScript, all 97 unit tests across 13 files, production build, all 26 account Chromium cases and 34 focused guest foundation/Start/difficulty cases in separate runs. All six required header layouts, keyboard/focus, 44px targets, touch and overflow pass; mobile/desktop signup-header screenshots were inspected. Two hosted browser checks also pass against the configured Supabase project: real Intermediate-bank report migration with original-byte/hash preservation, and empty signup without report rows. Temporary hosted test accounts were deleted. Source/built bank and approved Markdown hashes remain unchanged. Logs are test-results/signup-migration-{typecheck,unit-tests,build,account-browser,guest-browser}.txt; hosted evidence is test-results/signup-migration-hosted-browser.json and content evidence is test-results/signup-migration-content-integrity.json. Development server runs at http://127.0.0.1:5173. The account-enabled build has a nonblocking main bundle warning (about 776 kB; PDF module about 1,834 kB). This change does not establish a new full guest-suite run, production frontend deployment, SMTP verification or restore rehearsal. No SQL/function changes, commit or push occurred.
Hosted Supabase setup — 2026-10-14: The linked project records migration 202610140001, and backup-report, delete-report and delete-account are deployed and ACTIVE with platform JWT verification enabled. ARCHIVE_ALLOWED_ORIGINS permits localhost/127.0.0.1 at ports 5173, 4173 and 4210 for development/preview. All nine hosted CORS/anonymous-request checks and all 15 temporary-account integration checks pass: immediate no-confirmation signup, password login, real Intermediate-bank report backup/hash, idempotency/conflict protection, forged-owner rejection, owner-isolated reads/deletes/preferences, fresh-client snapshot recovery, current-password deletion, tombstones and independent per-owner attempt IDs. Both test accounts were deleted and their tokens rejected; no test accounts were retained. Evidence is test-results/cloud-history-hosted-{bootstrap,integration}.json. Frontend production deployment, production origins/Auth redirects, SMTP recovery, hosted browser/offline/PDF checks and operational backup/restore remain pending. No app code/test-suite rerun, commit or push occurred for this setup; only the three Edge Functions were deployed.
Supabase CLI repair — 2026-10-14: The Windows CLI binary was missing after npm exhausted disk space (ENOSPC). Cleared only generated project npm/Vite caches, installed and pinned Supabase CLI 2.120.0 with optional dependencies, and verified its version and login help through npm. The user has configured the frontend URL/publishable key and disabled email confirmation; hosted project linking, migration application, function deployment and smoke checks remain pending. Setup instructions now include repair guidance and remote function bundling via --use-api. This tooling change did not rerun app tests, commit, push or deploy.
Supabase account history — local implementation, 2026-10-14: Optional email/password accounts without email verification, completed-report backup/import, retries and pause/resume, account history recovery, JSON export, device/report/account management, immutable snapshots and owner isolation are implemented locally. Accounts remain disabled until the user-created Supabase project is configured. TypeScript, 93 unit tests across 13 files, production build, three Deno function checks, all 117 existing Chromium cases and all 21 separate account Chromium cases pass. Mobile/desktop account screenshots were inspected; six required layouts, keyboard, touch and overflow checks pass. Bank/approved-content hashes are unchanged. The SQL migration was tested in embedded PostgreSQL (PGlite); account browser tests use the real SDK against a synthetic service. Hosted Auth/RLS/function/SMTP checks, backup restoration and production rollout remain pending. See CLOUD_HISTORY_VERIFICATION.md and SUPABASE_SETUP.md. No commit, push or deployment occurred.
Final cloud/authentication decision — 2026-10-14: Keep Supabase and use email and password signup/sign-in only, without mandatory email verification. Username fields/mapping/login endpoints are removed from the planned release. The earlier Aiven comparison is historical reference, not a pending provider decision. CLOUD_HISTORY_IMPLEMENTATION_PLAN.md records the updated scope. This is a documentation-only update; no feature code, provisioning, tests or deployment occurred.
Authentication clarification and Aiven comparison — 2026-10-14: The user requires simple username/email and password signup/sign-in without mandatory email verification, replacing the plan's earlier OTP proposal. CLOUD_HISTORY_IMPLEMENTATION_PLAN.md now records that flow and an Aiven PostgreSQL alternative requiring separate application authentication/API work. Aiven is under consideration, not selected. No feature code, provisioning, tests or deployment occurred.
Supabase history planning — 2026-10-14: The user approved Approach 1: optional Supabase authentication and PostgreSQL backup of completed reports alongside browser persistence. CLOUD_HISTORY_IMPLEMENTATION_PLAN.md records the proposed account/import/synchronization model, immutable snapshots, ownership and retry safeguards, data controls, phased delivery, verification and operations. This is a plan only; no feature implementation, Supabase provisioning, tests, commit, push or deployment occurred. Detailed planning defaults remain proposals.
Documentation was synchronized with the current code on 2026-10-14. Intermediate is the new-attempt default; the selector uses Start Exam and the three approved comparison points. Results includes PDF export and the corrected Intermediate title. Earlier phase descriptions below retain their historical test totals, defaults and preview-port observations; they do not establish present server availability. The latest unit run passed 69 tests across 9 files. The last recorded full browser run passed 101 cases across 9 files on 2026-10-13; subsequent browser checks are focused runs. This documentation-only audit did not rerun tests, commit, push or deploy.
The user-approved difficulty comparison now has three bullets per level about reading load, interacting constraints and structured evidence (2026-10-14), based on comparison of both reviewed banks. Exact copy is in src/domain/difficulty.ts and IMPLEMENTATION_SPEC.md. TypeScript, build and 7 focused Chromium cases pass: both levels across six required layouts, keyboard/focus/overflow and touch start. Mobile/desktop screenshots were inspected; Intermediate images are screenshots/default-intermediate-*.png and Advanced images are screenshots/difficulty-comparison-advanced-{390,1366}.png. Logs are test-results/difficulty-comparison-{typecheck,build,browser}.txt. No unit-test rerun or deployment occurred for this copy-only change.
The difficulty selector's primary action now reads Start Exam for both Intermediate and Advanced (2026-10-14); its saving label remains Starting exam…. Browser locators use the static label, with difficulty chosen independently. Intermediate remains the default. TypeScript, build and all 18 difficulty Chromium cases pass, including six layouts, keyboard, touch and both real banks. Refreshed mobile/desktop screenshots were inspected. Logs are test-results/start-exam-label-{typecheck,build,browser}.txt. No new unit-test run or deployment occurred for this copy-only update.
Intermediate is now the default for every new-attempt difficulty dialog (2026-10-14), including Start exam, Start another exam and Results Start new attempt. Cancelling an Advanced selection and reopening resets to Intermediate. Advanced remains available through explicit selection. Existing attempts, reviewed banks and the Source courses drawer's initial selection are unchanged. Browser tests for Advanced flows now select Advanced explicitly. Verification passed TypeScript, 69 unit tests, build and 34 targeted Chromium cases across difficulty, Start and foundation suites. Six required selector layouts pass; mobile, desktop, landscape and enlarged screenshots screenshots/default-intermediate-*.png were inspected. Logs are test-results/default-intermediate-{typecheck,unit-tests,build,browser}.txt. Both reviewed bank hashes are unchanged. This is targeted verification, not a new full browser-suite result. No deployment occurred.
BUG-007 is fixed locally on 2026-10-14: Intermediate Results and PDF reports display Independent PCAO-F Practice — Intermediate through a shared presentation helper. Reviewed banks, frozen snapshots and Advanced/legacy/custom titles remain unchanged. TypeScript, 69 unit tests across 9 files and build pass. All six required report layouts, keyboard/focus/overflow checks, actual Intermediate PDF content and touch export pass; mobile/desktop screenshots screenshots/bug-007-report-title-{390,1366}.png were inspected. Logs are test-results/bug-007-{typecheck,unit-tests,build,browser}.txt. The Advanced PDF case initially exceeded its 30-second timeout, then passed with a 120-second limit (30.2 seconds); see test-results/bug-007-pdf-rerun.txt. All 9 targeted browser cases pass across the main run and focused rerun. This is targeted verification, not a new full browser-suite result. No BUG-007 deployment occurred.
Result-report PDF export is complete locally on 2026-10-14. Results now has Export report (PDF), downloading a formatted A4 report with scores, metadata, domain tables, priorities and all saved question reviews regardless of the active filter. Markdown renders as readable text, lists and tables; embedded fonts, page numbers, question bookmarks and web source links are included. Table rows and review headings stay with their content. Generation has a busy state, keyboard focus restoration and retryable errors. Advanced, Intermediate and legacy reports work offline once the Results PDF module has loaded; exporting does not write storage. Verification passed TypeScript, 65 unit tests across 9 files, build and 13 focused Chromium cases (10 export cases plus 3 existing Results regressions). Actual PDF content, all six required layouts, keyboard, touch, retries, URL cleanup and heading pagination passed. Generated PDFs were rendered and visually inspected, with clean all-page margin/glyph/footer audits; UI screenshots screenshots/report-export-{390,1366}.png were inspected. Logs are test-results/report-export-pdf-{typecheck,unit-tests,build,browser}.txt; PDF layout evidence is test-results/report-export-pdf/layout-audit.json. This targeted run does not replace the prior full-suite browser evidence below. Reviewed-bank and approved-content hashes remain unchanged. Preview port 4210 is running; no export deployment occurred.
Historical Intermediate integration completed locally on 2026-10-13 following UI approval and uploaded-bank validation. Advanced (the default at that phase) uses public/question-bank.json 1.1.0; Intermediate uses public/easy-question-bank.json 1.0.0. Both reviewed banks are enabled and load independently; one unavailable bank does not block the other. Source courses, attempts, grading, Results and history use the selected/original snapshot. Bank bytes and legacy records were not migrated. Latest verification passed TypeScript, 55 unit tests across 8 files, build and all 101 Chromium cases across 9 files in one final full run. Six selector layouts, actual Intermediate mobile/desktop flows, touch, confirmed replacement, retries and response races pass; new screenshots were inspected. See INTERMEDIATE_INTEGRATION_VERIFICATION.md and test-results/intermediate-{typecheck,unit-tests,build,browser-checks}.txt. The updated preview on port 4210 remains running. No deployment occurred.
Historical validation step: the difficulty UI was user-approved. The uploaded public/easy-question-bank.json passes the actual app validator with requireReviewed=true: version 1.0.0, exam ID pcao-f-practice-easy-01, reviewed, 60 questions, 120 minutes and the prescribed seven domain totals. SHA-256 is f2a4f36183f2a30f79b83fc928389178e13151ea77a955819c890caf32992be4. See EASY_BANK_VALIDATION.md and test-results/easy-bank-validation.json. Runtime integration remains pending; the bank and difficulty catalog were not modified during validation. This does not imply a new full-app test/build run.
Historical Phase 1: the difficulty-selection implementation on 2026-10-13 is complete locally and ready for UI review. All new-start actions open an Intermediate/Advanced toggle dialog with comparison bullets. Advanced uses the unchanged reviewed bank; Intermediate is selectable but cannot start until its second reviewed bank is supplied after UI approval. Preparation and replacement share one modal, expire through the existing finalizer and preserve saving safeguards. New attempts save immutable difficulty metadata, shown in Exam, Results and history; legacy records show Original exam without migration. Source courses identify the selected level. See DIFFICULTY_MAPPING_CORRECTION.md. Latest checks passed TypeScript, 53 unit tests across 8 files, build and all 95 Chromium cases across 9 files in one final full run. Mobile/desktop and scrolled narrow/enlarged screenshots were inspected. The local preview on port 4210 is left running for UI review. UI approval and uploaded Intermediate bank validation are complete; runtime integration is pending. No deployment was performed.
The reviewed bank is version 1.1.0: 60 questions, 120 minutes, and SHA-256 e1a9f7b3f157320ea738a0a06a48327439b2d89902da64137ca03dddc7d2efea. It validates and permits local attempts. The four views, browser persistence, fixed-deadline timer, exact-set grading, and frozen attempt snapshots are implemented.
The Start page includes exam details, instructions, Start/Resume controls, and compact browser-local attempt history with completion dates, practice scores out of 1000, Passed/Failed badges, correct/total and report actions. New starts revalidate and freeze the loaded bank before persistence and opening Q1. Repeated Start clicks are guarded; replacing an unfinished attempt requires confirmation and restores prior records if a storage write fails. Resume retains the original answers, flags, snapshot, position, and deadline; expired resumes use the shared submission function and open results. Loading, empty, and storage-failure states are implemented, and item-level data remains hidden on the Start page.
The timed Exam page has a persistent title, answered count (exact required cardinality), and deadline-based timer. It saves answers, flags, and navigation immediately; supports radio clearing, checkbox deselection and limits; hides domains and review material; and focuses the question heading after navigation. Visibility, focus, page-show, interval, and edit checks use shared finalization at expiry, including automatic recovery on reopening. Markdown renderer identities stay stable so timer updates preserve table focus and scroll. Review and submit uses src/ui/ReviewSubmitDialog.tsx with shared selection-status and submission helpers.
Review and submit has a status legend, independent flags/current markers, all 60 question numbers, counts, time remaining, confirmation with flagged reminders and expiry precedence. Only the modal countdown is visible while a timed dialog is open. Unfinished, unexpired attempts use native browser leave warnings where supported; Attempt history from Exam opens a focus-managed leave confirmation, and home explicitly reminds that the timer continues. Expiry closes the leave dialog and finalizes through the existing helper. Focus trapping, Escape, opener restoration and question-heading jumps are implemented. Submission saves a validated grade once and opens read-only Results.
Results uses the shared grader and frozen snapshots, with exclusive outcome counts, capped elapsed time, submission metadata, seven domain percentages and up to three practice review priorities. All/Incorrect/Unanswered/Flagged filters and initially collapsed native disclosures show exact reviewed content, per-option outcomes and explanations, and supplied course sections/links. Before submission, blank or insufficient selections are Unanswered and exact cardinality is Answered; flags/current remain independent. After submission, blank responses are Unanswered and every nonblank nonexact set is Incorrect, with selected and correct sets visible and no incomplete-selection badge. Results headline an equal-weight Practice score out of 1000 and practice Passed/Failed at the unrounded 720 threshold; correct/total is secondary. History derives the same values from original snapshots without stored-grade migration. The home Source courses drawer shows ten deduplicated course title/URL pairs from the validated bank, with focus trapping, dismissal/restoration and mobile scrolling, without question-specific references or storage changes. The three outcome counts total 60. Domain review priorities rank unrounded percentages and retain snapshot order for ties. Approved Markdown integrity and independently specified synthetic overall/domain results are tested. See PROJECT_IMPLEMENTATION_STATUS.md for current evidence; RESULTS_VERIFICATION.md remains historical.
The earlier reliability-fix verification on 2026-10-13 passed TypeScript, 46 Vitest tests across 7 files, production build, and all 83 Chromium cases across 8 files in one full run. Shared browser locks and record revisions prevent stale-tab overwrites and synchronize submitted reports. Start-page expiry preserves reports, existing indexed edits use one write, new-record failures roll back, and timestamp recovery rejects damaged metadata. Legacy records remain readable. Coverage includes simultaneous tabs, partial writes, damaged dates, repeated starts, focus, refresh/expiry recovery, original snapshots, touch, and 320, 390, 768, 1366, landscape and enlarged-text layouts. New mobile/desktop submission-notice screenshots were inspected. Bank and approved-content hashes are unchanged. The app has not been published. The earlier 2026-10-12 QA-fix record remains historical.
Documentation and Verification Records
The historical project analysis on 2026-10-13 reran TypeScript, all 46 unit tests and build successfully. The unchanged Chromium suite passed 80/83 cases; two failures compared storage before a flag save completed, and one reloaded before awaiting submission completion. A sequential rerun passed the two flag cases and reproduced the reload case. Diagnostic copies with explicit waits passed all three cases three times (9/9); original app/test source was unchanged. This does not supersede the historical 83/83 reliability-fix run with a new clean full-suite result. See PROJECT_ANALYSIS.md and the project-analysis section of PROJECT_IMPLEMENTATION_STATUS.md. An isolated Chromium capacity check reached quota after 27 full attempt records; export/deletion and storage management remain proposed improvements.
PROJECT_IMPLEMENTATION_STATUS.md is the unified handoff document for all implementations, project status, data rules, test results, screenshots, local commands, and remaining work. Keep it and this status section aligned when behavior or verification changes. Preserve earlier phase reports as historical records; their test totals are successive suite snapshots, not cumulative counts.
The earlier corrected-mapping Chromium log is test-results/difficulty-mapping-browser-checks.txt; TypeScript, 53 unit tests and the build were rerun successfully. Earlier Phase 1 verification logs are test-results/difficulty-{typecheck,unit-tests,build,browser-checks}.txt, documented in DIFFICULTY_VERIFICATION.md. The earlier reliability log is test-results/reliability-fix-checks.txt. The earlier QA-fix log is test-results/qa-audit-fix-checks.txt; test-results/results-checks.txt preserves the earlier clean 60-case full run. Unit coverage now spans 9 files and browser test sources span 11 files; the historical full browser run covered 9 files. Chromium is the verified browser; Firefox/WebKit and a formal independent accessibility audit are not recorded. The production build passes with a nonblocking warning for the approximately 520 kB main JavaScript bundle and a separate approximately 1,834 kB Results PDF module. Reliability-fix checks used port 4192; the prior QA-fix checks used port 4180; those temporary previews were stopped afterward. The earlier UI-update preview used port 4178 (earlier Results checks used 4177); the normal browser-test configuration uses port 4173. The earlier UI update reran typecheck, unit tests, build, and all 60 browser tests; a focused passing rerun added score/history screenshots. Later documentation-only updates do not imply a new test run.
The approved Markdown reference is tests/fixtures/approved-question-content.md, SHA-256 7cf994a46f4dbb36951a2ae943f43e288025676e2937703007c8a3930e89c729. All 60 stems, ordered options, IDs, and selection counts match it. Historical correction-report app-integration prerequisites are complete. Publishing remains a later step; no public deployment is established.
Build, Test, and Development Commands
Run npm ci for locked dependencies. npm run dev starts Vite; npm run preview serves the production build. Run npm run typecheck for TypeScript, npm test for Vitest, npm run build to create dist/, and npm run test:browser for Playwright. Install Playwright Chromium first if it is missing.
Coding Style & Naming Conventions
Use two-space indentation, semicolons, strict types, and functional React components. Name component files in PascalCase (for example, MarkdownContent.tsx), functions in camelCase, and unit tests *.test.ts(x). Keep question content out of components. No formatter or linter is configured; match nearby code.
Testing Guidelines
Add meaningful Vitest cases when validation, scoring, persistence, or snapshot rules change. Use clearly marked synthetic test fixtures. For UI changes, verify Playwright interactions at 320, 390, 768, and 1366 pixels, plus mobile landscape and enlarged text. Inspect mobile and desktop screenshots in screenshots/; check keyboard access, touch targets, and page-wide overflow.
Question Data & Safety
Require the complete reviewed bank to validate before starting a new attempt. Never invent answer keys, explanations, sources, official scaled scores, certification pass/fail predictions, or readiness assessments. Equal-weight practice score and practice Passed/Failed at 720/1000 are explicitly authorized. Preserve approved Markdown, option order, corrected Q23/Q24, and unchanged Q45. Do not execute raw HTML from question content. Existing attempts must keep their frozen snapshots. Local JSON changes become public only after deployment.
Commits & Pull Requests
Git is initialized locally on main, with origin configured for https://github.com/MuhammadRaedSiddiqui/pcao-f-practice-exam.git. Use short imperative subjects, such as Validate question option IDs. PRs should explain behavior and data changes, link an issue when available, report checks, and include mobile and desktop screenshots for UI changes.
Vercel deployment preparation
Production deployment is now user-authorized. vercel.json defines Vite, npm ci, npm run build, and dist/; .vercelignore preserves source/runtime inputs and excludes local artifacts. A fresh build passed, but the sandbox CLI deployment failed with outbound HTTPS EACCES on port 443. No live URL or production smoke check is confirmed. See the Vercel section in PROJECT_IMPLEMENTATION_STATUS.md for commands and evidence.
Coordinated persistence
Browser storage mutations use src/domain/browserStorage.ts to hold one Web Lock covering records and history. Preserve the revision checks and submitted-report immutability in saveAttempt; older records without revisions use revision 0 for the next edit. Keep pending answer feedback separate from committed selections. Saved unfinished attempts must expire on Start as well as Exam, and expired reports must be preserved before replacement. Validate date ranges and submission/deadline relationships during recovery. Saving requires Web Locks; browsers without coordinated saving retain access to saved reports.
Difficulty maintenance
.gitattributes pins both runtime banks and the approved Markdown reference to LF so their reviewed checksums remain stable across Windows/Unix checkouts. Preserve these rules when changing Git configuration.
Intermediate is the default whenever the new-attempt difficulty selector opens. The Source courses drawer shows one shared list without a difficulty toggle, derived from either ready validated bank; this does not change exam bank selection. Both reviewed banks are enabled: Advanced stays at public/question-bank.json; Intermediate stays at public/easy-question-bank.json. Preserve their reviewed content and hashes, and publish a new dataset version for future content/key/explanation/source edits. Require complete independent validation before each new attempt. Preserve optional difficulty recovery, immutable saved difficulty and frozen snapshots; older records remain Original exam. Keep per-level loading/retry independent and never substitute one bank for the other. UI approval and Intermediate integration are complete.
