Examulator — Changelog

Consolidated release history for the Examulator solution (both product generations, all supporting projects). This file supersedes the previous per-generation Examulator/CHANGELOG.md and Examulator3/CHANGELOG.md files (which were maintained as byte-identical mirrors and are removed as of 26231.01-closed).

Entries are ordered newest-first. Each release heading carries a scope line naming the components touched by that release; individual subsections may narrow that scope further via a project-list tag under the subsection heading.

Product components at a glance

Gen2 (com.certificationtoolanddie.examulator) — Examulator (PC/web) · ExamulatorCore (shared logic) · ExamulatorMaui (Android) · ExamulatorApi (Contribute Results endpoint) · ExamulatorDiag · ExamulatorTrace · ExamulatorUpdater · ExamulatorDiagnostics · ExamulatorTests · ExamulatorIrt (shared with Gen3)

Gen3 (com.certificationtoolanddie.examulator3) — Examulator3 · Examulator3Core · Examulator3Maui · Examulator3Api · Examulator3Diag · Examulator3Trace · Examulator3Diagnostics · Examulator3Tests

The four surfaces referenced by many entries are: PC Gen2 (Examulator), PC Gen3 (Examulator3), MAUI Gen2 (ExamulatorMaui), MAUI Gen3 (Examulator3Maui).

[26286.01] — 2026-10-13 (current)

Scope: Quiz domain balance — Domain 0 excluded from quizzes and multi-domain quizzes allocated by blueprint weight. ExamulatorCore · Examulator3Core (selection) · Help on all four surfaces. versionCode → 2628601.

Android release following a user report that CISA quizzes under-represented Governance. Investigation confirmed the CISA Domains.percentage values and the Linear allocation were correct; the quiz path was not blueprint-aware.

Fix — "All Domains" quizzes included Domain 0 (unscored pretest) items

ExamulatorCore · Examulator3Core

"All Domains" sends no domain filter, so the quiz query drew from every domain — including Domain 0, which holds the unscored/beta pretest items reserved for CAT. Those items (≈7.6% of the CISA pool) were served and scored in quizzes and diluted every real domain's share (Governance fell to ~16.7% against an 18% weight). GetQuizQuestionsAsync and CountForQuizAsync now exclude Domain 0 explicitly. The quiz domain pickers already hid Domain 0, so single-domain and hand-picked selections were never affected; Linear and CAT are unchanged.

Change — quizzes now follow the exam-outline domain weights

ExamulatorCore · Examulator3Core

Quizzes previously drew one random sample from the eligible pool, so the domain mix tracked how the question bank happened to be built rather than the exam outline — fine for a balanced pool like CISA, but a domain holding 21% of a pool while weighted 10% would be over-served roughly two-to-one. When more than one scored domain is in play ("All Domains" or a multi-domain selection), the requested count is now allocated across the selected domains by their Domains.percentage weights, renormalised to the selection, using the same largest-remainder (Hamilton/Hare) method as Linear:

Single-domain quizzes, and exams with no usable weights, keep the plain random draw. The Help pages (Quiz Configuration on PC; Quiz mode on MAUI) describe the new split.

Fix — Domains.percentage values written with a "%" were read as zero

ExamulatorCore · Examulator3Core

ParsePct used NumberStyles.Any, which does not accept a percent sign, so a value stored as 18% parsed as 0 and that domain was silently dropped from Linear (and now quiz) allocation. The parser now trims whitespace and a trailing % first. Current pools store bare numbers, so no shipped exam was affected.

[26284.01] — 2026-10-11

Scope: Fix the Windows "file being used by another process" failure when installing a question-pool update. ExamulatorCore · Examulator3Core (installer). versionCode → 2628401.

Fix — "Install Update" failed on Windows with "the process cannot access the file … because it is being used by another process"

ExamulatorCore · Examulator3Core

Installing or updating a pool could fail at the final save step with a file-in-use error (e.g. Could not save database: … 'CCSP.db' … being used by another process). The exam databases are opened ReadOnly with a shared cache and a 128 MB memory-map, and Microsoft.Data.Sqlite pools connections by default, so a disposed read connection (from the version check, the query repositories, or the Select-Exam "Pool Version" display) keeps the Windows file handle and the memory-map open — blocking the installer's delete/overwrite of the existing .db. The installer now releases pooled handles (SqliteConnection.ClearAllPools()

Note — pool user_version must match the release. The app decides whether a newer pool exists by comparing the release/manifest version against each database's PRAGMA user_version, which is stamped by the pool-build pipeline (the app only reads it). A pool published as a new version but still carrying an older user_version will show a perpetual "update available" banner and the wrong Pool Version. Stamp each pool to its release version at build time (PRAGMA user_version = <YYDDD>;); the stamp_pool_version.py helper can check or re-stamp an existing set. Updated the CI/CD pipeline to automatically re-stamp the database files upon build. Previously the pipeline only re-stamped the internal version number if there was an actual database change, which let to a mismatch between the internal database version and the external file name version.

[26283.01] — 2026-10-10 (current)

Scope: ISACA quiz scaling fix; Select-Exam installed-version display and long-press update. All four surfaces (scoring + PC cards); ExamulatorMaui · Examulator3Maui (long-press).

Android release rolling up the Select-Exam enhancements and the ISACA quiz-scoring fix made since 26281.03. versionCode → 2628301.

Fix — ISACA quiz reported a scaled score above its own maximum (e.g. 920/800)

ExamulatorCore · Examulator3Core (scoring) · all result surfaces

Quiz finalisation computed the score as raw-ratio × 1000 and used the ISC2 pass line (700) for every agency, while the results screen displayed the agency maximum — so an ISACA quiz (e.g. 23/25 on CISA) showed 920 / 800. Quiz now maps the raw ratio through the exam's agency profile (AgencyScoringProfile.RatioToScaled) exactly as Linear does: ISC2 is a straight ×1000 and is unchanged, while ISACA maps onto 200–800 with the pass line at 450 (70% raw). 23/25 on an ISACA quiz now reads ~707/800, and the quiz pass/fail verdict uses the ISACA 450 line instead of passing at ~45%. The "How this score was computed" note on the results and historical-results screens, and the Help scoring tables, were made agency-aware to match (they no longer assert a flat 0–1000 for every exam). Already-recorded results keep their stored score; this corrects newly completed quizzes.

Add — installed pool version on the Select Exam cards

Examulator · Examulator3 · ExamulatorMaui · Examulator3Maui

Installed exam cards now show the installed question-pool version (the pool's PRAGMA user_version): MAUI renders it inline on the badge (✓ Installed · v26280); the PC cards add a "Pool Version" stat alongside Questions / Time Limit / Modes.

Add — long-press an installed exam to update its pool (MAUI)

ExamulatorMaui · Examulator3Maui

When the manifest reports a newer pool for an installed exam, the card shows an "⬆ Update available" badge; long-pressing (the touch contextmenu gesture) downloads and installs the update in-app through the same DatabaseDownloadService path as a fresh install, with inline progress. A normal tap still starts the exam. The PC surfaces already expose an explicit "Install Update" button, so they were left unchanged.


[26281.03] — 2026-10-08

Scope: Android app icon, in-app Play updates, and update-dialog download fix. ExamulatorMaui · Examulator3Maui.

Android-focused release over 26281.02. Restores the real app icon, adds Google Play in-app updates, and fixes the launch "Question Bank Updates" dialog so it installs updates in-app instead of bouncing the user to a Google sign-in page. versionCode bumped to 2628103 for the Play upload.

Fix — app icon reverted to a generic "E"

ExamulatorMaui · Examulator3Maui

The MAUI app icon had been pointing at placeholder appicon.svg / appiconfg.svg files that drew a plain gold "E", so production builds shipped with a generic icon instead of the Examulator logo. Re-cut the icon from the real brand art as the gear/shield badge (company text removed, sized inside the Android adaptive safe zone) on the navy brand background, and repointed MauiIcon at the PNG sources. Opaque base (no alpha) keeps the iOS icon set valid; transparent-corner foreground drives the Android adaptive icon. The full logo-with-text is retained as source art outside the icon pipeline.

Fix — "Question Bank Updates" dialog failed with a Google login / "unsupported file type"

ExamulatorMaui · Examulator3Maui

The launch update dialog offered each pool update as an external hyperlink (<a href target="_blank">) to the manifest's Google Drive URL. In the MAUI WebView that is a navigation, so Google served a sign-in/consent page and the app received HTML rather than the .db ZIP — surfacing as "This platform does not support this file type." The dialog now downloads through the same in-app DatabaseDownloadService the Select Exam screen uses (HttpClient + Drive-URL rewrite + ZIP extraction), with inline progress and a success/failure banner. No WebView navigation, no Google login. The Select Exam path and the PC update banner were already correct and are unchanged.

Add — Google Play In-App Updates (flexible flow)

ExamulatorMaui · Examulator3Maui

Added Platforms/Android/InAppUpdateService.cs, wired into MainActivity (OnResume), surfacing Play's flexible in-app update prompt and completing a background-downloaded update on return to the app. Android/Play only — no-ops for sideloaded copies; iOS and the PC builds are unaffected. New Android-only package Xamarin.Google.Android.Play.App.Update.

Fix — R8 dex failure after adding the Play update package

ExamulatorMaui · Examulator3Maui

The Play App Update package pulled AndroidX Fragment up to 1.9.0, where the Kotlin-extension FragmentKt class was merged into the main fragment artifact. With Xamarin.AndroidX.Fragment.Ktx still at 1.8.x, FragmentKt existed in both AARs and R8/D8 failed with "androidx.fragment.app.FragmentKt is defined multiple times." Pinned both Xamarin.AndroidX.Fragment and Xamarin.AndroidX.Fragment.Ktx to 1.9.0 (where fragment-ktx is an empty forwarding stub) to resolve the duplicate.


[26281.02] — 2026-10-08

Scope: In-app Help — new Support section across all four surfaces. Examulator · Examulator3 · ExamulatorMaui · Examulator3Maui.

Content-only patch over 26281.01 (which went to the Android push). No engine, scoring, or data changes — the revision was bumped so the new Android build carries a higher versionCode (2628102) than the 26281.01 upload.

Add — "Support" section on the Help page

Examulator · Examulator3 · ExamulatorMaui · Examulator3Maui

Added a dedicated Support section (now §2, directly after Getting Started) to the Help page on all four surfaces, listing the three ways to reach us:

…plus a reminder to include screenshots, reproduction steps, and any other relevant material when reporting a bug so issues can be diagnosed quickly. Subsequent Help sections were renumbered 3–12 (the PC quick-jump table of contents gained a Support link, and the internal §-cross-references for CAT & IRT and Configuration were updated to match).

Change — consolidated the PC bug-report FAQ into Support

Examulator · Examulator3

Removed the standalone "How do I report a bug or suggest a feature?" FAQ item from the PC surfaces; its one unique detail — for exam-result bugs, include the ExamulatorDiag export — now lives in the Support section's bug-reporting note, cross-linked to §9 Diagnostic Tools. (The MAUI Help FAQ never carried this item, and Android has no ExamulatorDiag tool, so the MAUI Support section stands on its own.)


[26281.01] — 2026-10-08

Scope: Android import + Play-Store optimization fixes; rebrand completion. ExamulatorMaui · Examulator3Maui (+ shared rebrand across all surfaces).

Patch over the first production build (26280.01), addressing a user-reported Android import failure and the Play Console DEX-optimization advisory.

Fix — "File picker error: This platform does not support this file type" (Android import)

ExamulatorMaui · Examulator3Maui

Importing an exam database from device storage ("Import from file") threw "File picker error: This platform does not support this file type" and never opened the picker. Cause: the MAUI FilePicker was given an Android FilePickerFileType of */*; a .db has no registered Android MIME type, so SAF's type resolution throws (and a filter greys out .db files on some file managers anyway). Fixed by removing the file-type filter entirely — the picker opens to all files and the existing post-pick validation (must be .db, a known exam code, and a valid SQLite header) gates the import. Both MAUI gens.

Fix — ISACA exams rejected on import

ExamulatorMaui · Examulator3Maui

The import allow-list predated ISACA support, so importing an ISACA .db (CISA, CDPSE, CCOA, CCA, CCP, AAIA, AAIR, AAISM) was rejected as "not a recognised exam database". Both gens now derive the ISACA half of the allow-list from LegacyExamAgencyMap.IsacaCodes — the same source of truth the agency tabs and scoring use — so it can't drift again. The Gen2 rejection message is now agency-neutral.

Build — opt-in R8 obfuscation for the Play DEX-optimization advisory

ExamulatorMaui · Examulator3Maui

Added <AndroidR8ObfuscationMode>private-members</AndroidR8ObfuscationMode> (Android only). .NET for Android ships R8 with obfuscation off, which is why Google Play reports ~1% (its 25%-by-Feb-2027 threshold). private-members renames private/package-private members only — public + JNI-callable names stay intact — raising the metric without risking the interop bridge. Requires a .NET 10.0.1xx servicing SDK (or .NET 11, where it is the default); ignored on older SDKs. Verify on an internal-testing track before production.

[26280.01] — 2026-10-07

Scope: First production build. ISACA agency support, four-band difficulty standardization, and the ISC2 rebrand across all four surfaces + shared ExamulatorIrt; PC Help and exam-download fixes.

First production (General Availability) build, promoted from the -rc1 Early-Access line. Per app-store convention the production channel carries no suffix — the pre-release tags (-alpha, -closed, -rc1) are dropped and the version ships as the bare 26280.01. It bundles everything landed since the 26274 release candidate: ISACA question-pool support, the ISC2 rebrand, the four-band difficulty standardization, and the PC Help/download fixes below.

Feature — ISACA question-pool support (per-agency scoring)

ExamulatorIrt · ExamulatorCore · Examulator3Core · Examulator · Examulator3 · ExamulatorMaui · Examulator3Maui

Gen2 can now surface and score ISACA exams (CISM, CISA, CRISC, CDPSE, CGEIT, CCOA, AAIA, AAIR, AAISM) alongside its ISC2 pools, with the scoring engine also wired into Gen3 so both generations stay aligned.

The Help pages on all four surfaces describe the ISACA 200–800 / 450 model alongside ISC2.

Difficulty bands standardized to four across every agency. ISC2 publishes no difficulty labels and its previous five-band split (…Advanced…) was Examulator's own invention, so both agencies now use ISACA's four bands over the raw 1–253 scale — Easy (1–63), Moderate (64–127), Difficult (128–190), Expert (191+). This changes only presentation and the quiz difficulty filter (now four options); raw difficulty values and IRT scoring are untouched. DifficultyLevel (both Core projects), the shared band table in ScoringProfiles, the difficulty-balanced Linear allocator, and all difficulty reports/breakdowns were updated together.

Fix — Help sections not expanding (PC)

Examulator · Examulator3

The collapsible Help sections had the CSS (.help-section.collapsed) and the chevron affordance but no script to toggle them, so clicking a header did nothing. Added the toggle script (click + keyboard) to both PC Help pages. The MAUI Help pages already toggled via a Blazor @onclick handler and were unaffected.

Fix — exam-database download buttons missing on PC

Examulator · Examulator3

The Select-Exam page read the version-manifest result immediately after a fire-and-forget start, so on the first (and only) server render the fetch had usually not finished and every DownloadUrl was null — no Install buttons for any exam. The page now awaits the manifest check before building the card list (memoized, 8s timeout), so install/update buttons appear on first load. The MAUI pages already polled and re-rendered.


[26274.01-rc1] — 2026-10-01

Scope: iOS (both MAUI gens).

iOS 27 launch-crash fix, plus assorted iOS App Store packaging fixes discovered while shipping the first iOS build (the TestFlight release candidate). Revision bumped so the build number exceeds the earlier (faulty) App Store Connect upload.

Fix — iOS 27 crash on launch (UIScene lifecycle not adopted)

ExamulatorMaui · Examulator3Maui

First iOS release crashed immediately on launch on iPhone/iPad running iOS 27 (EXC_BREAKPOINT / SIGTRAP in _UIApplicationEvaluateRuntimeIssueForNoScene LifecycleAdoption), while running fine on iOS/iPadOS 26. Apps linked against the iOS 27 SDK (Xcode 27) must adopt the UIScene lifecycle; iOS 26 only warned, which is why dev testing on an iPadOS 26 device missed it. Fixed per the official MAUI template remedy (dotnet/maui #38601): added UIApplicationSceneManifest to Platforms/iOS/Info.plist (single window scene, config name __MAUI_DEFAULT_SCENE_CONFIGURATION__, delegate SceneDelegate) and a Platforms/iOS/SceneDelegate.cs ([Register("SceneDelegate")] : MauiUISceneDelegate). Both gens.

Fix — iOS App Store packaging (icons, version string, splash)

ExamulatorMaui · Examulator3Maui

Resolved the issues that blocked the first App Store upload: - App icons (90022/90023/90713): hand-written Platforms/iOS/Info.plist doesn't receive actool's auto-merged icon keys, so added CFBundleIconName (appicon) + CFBundleIcons/~ipad explicitly; MauiIcon switched to the flat SVG source. - Version string: CFBundleShortVersionString must be 1–3 integers, so an iOS-only ApplicationDisplayVersion override emits Apple-format YYDDD.V (e.g. 26274.1) while Android keeps the full YYDDD.VV-channel scheme. - Splash: removed <text> from splash.svg (Resizetizer/SkiaSharp crash on SVG text). - Linker: SupportedOSPlatformVersion values made inline (a newline in the value broke custom-linker-options.txt → MT2301). - Signing: manual iOS code-signing block pinned in the csproj.

Infrastructure — Version bumped to 26274.01-rc1 (13 sites)

Examulator · ExamulatorMaui · Examulator3 · Examulator3Maui · ExamulatorDiag · Examulator3Diag · ExamulatorTrace · Examulator3Trace

Julian date bump 26272 → 26274 (2026-10-01). Channel unchanged (rc1). MAUI <ApplicationVersion> = 2627401. 13th site: the iOS-only ApplicationDisplayVersion override (Apple format) is now part of the bump — 26274.1. Bumped specifically so CFBundleVersion exceeds the earlier faulty App Store Connect upload (Apple rejects duplicate/lower build numbers).


[26272.01-rc1] — 2026-09-29

Scope: Both generations, all four surfaces (except where noted).

Bug-fix and feature release. Fixes two report defects reported by a CCSP user, completes the CPE report on all surfaces, adds an in-app release-notes viewer, and lands iOS build/signing configuration.

Fix — CAT Ability Trajectory (and all mode-filtered reports) showed no CAT sessions

ExamulatorCore · Examulator3Core

ExamMode.CAT.ToString() persists the mode as "CAT" (all caps), but ReportDataService.ModeAllowed matched it case-sensitively against "Cat" — so "CAT" != "Cat" and every CAT session was silently excluded from any mode-filtered report's session picker. This made CAT Ability Trajectory (a CAT-only report) show zero eligible sessions for every user since it shipped, and also under-counted CAT sessions toward other reports' eligibility. Linear and Quiz were unaffected (their enum names match their stored strings). Fixed by making ModeAllowed case-insensitive in both gens.

Fix — Reports wrongly reported "not enough data" for CAT-only users

ExamulatorCore · Examulator3Core

A second instance of the same "CAT"-vs-"Cat" casing defect, in the eligibility path. GetExamSummariesAsync computes each exam's Linear/CAT session and question counts with SQL CASE WHEN mode IN ('Linear','Cat'), but the persisted value is 'CAT' and SQLite string comparison is case-sensitive — so CAT sessions were excluded from the counts. Those counts feed CoarseShortfall, the gate that decides whether a report has enough data, so a user with ample passed CAT exams (e.g. CCSP, a CAT-native cert) saw LinearCatSessions = 0 and every multi-session Linear/CAT report (Score Progression, Domain Trends, Readiness Snapshot, Persistent Gaps, etc.) incorrectly reported "not enough data." Fixed by making the summary query's mode comparisons case-insensitive (UPPER(mode) IN ('LINEAR','CAT')) in both gens. An exhaustive sweep confirmed no other case-sensitive mode-string comparisons remain (all other mode checks are enum-based).

Fix — Exam History kebab (⋮) menu rendered off-screen / behind rows

Examulator · Examulator3

The Exam History row-action menu (used for PDF export, delete, diagnostic export) is styled position: fixed (viewport-relative) but was positioned in JavaScript by adding window.scrollX/scrollY, which is only correct for position: absolute. On any scrolled page the menu was offset by the scroll amount and landed off-screen or behind other rows — only the bottom row's menu appeared usable. Fixed to use pure viewport coordinates from getBoundingClientRect(), with flip-up-when-near-bottom and left-edge clamping so the menu always stays on screen. Both PC gens. (MAUI is unaffected — it uses native action sheets.)

Fix — CPE Report on Android printed no recipient name

ExamulatorMaui · Examulator3Maui

The MAUI Configure page passed the recipient name via URL query (&recipient=), but the Output page never parsed it back, so _recipientName stayed empty and the certificate omitted the name. Added the missing query-parse in both MAUI Output pages.

Fix — CPE Report did not generate on Windows

Examulator · Examulator3

The PC Output view had a title-line reference to the CPE report but no render block for the report body — the else if (Model.Kind == ReportKind.CpeReport) section was never added, so generating a CPE report on the PC surface produced no output. Added the full certificate render block (masthead, certificate text, session table with totals, Download PDF button, disclaimer) to both PC gens; Gen3 includes the agency field its PDF handler requires.

Feature — In-app release-notes viewer on the update screens

ExamulatorCore · Examulator3Core · Examulator · Examulator3 · ExamulatorMaui · Examulator3Maui

New ReleaseNotesService fetches the published changelog page and shows "what's changed since your version" directly on the update-available screen, so users see what an update contains before installing rather than updating blindly. Extracts the page's <div role="main"> via balanced-tag matching and slices every release newer than the running version (versions are descending, newest first). MAUI shows it on the question-bank update dialog; PC shows it on the app-update banner as an expandable panel. Configurable via UpdateCheck:ReleaseNotesUrl. Every failure degrades to a "view online" link and never blocks the update. Notes: extraction runs against Google Sites HTML (no raw-markdown endpoint available there) and should be validated against the live page; PC injects the fetched fragment via MarkupString (source is the app's own controlled changelog).

Infrastructure — iOS build & signing configuration

ExamulatorMaui · Examulator3Maui

Manual iOS code-signing block added to ExamulatorMaui.csproj (ProvisioningType=manual, Apple Distribution key, com.certificationtoolanddie.examulator profile) so it persists across source drops. Removed earlier experimental MtouchLink/PublishTrimmed overrides that were causing (not fixing) an MT2301 linker error — the actual cause was Windows↔Mac toolchain version drift, resolved by matching workloads and clearing the PairToMac cache. Note: current .NET 10 iOS tooling requires Xcode 27 / Apple Silicon; Intel Mac build hosts are no longer supported by the toolchain.

Infrastructure — Version bumped to 26272.01-rc1 (12 sites)

Examulator · ExamulatorMaui · Examulator3 · Examulator3Maui · ExamulatorDiag · Examulator3Diag · ExamulatorTrace · Examulator3Trace

Julian date bump from 26258 to 26272 (2026-09-29). Channel unchanged (rc1). MAUI <ApplicationVersion> = 2627201 (26272 × 100 + 1).


[26258.02-rc1] — 2026-09-15

Scope: Both generations, all four surfaces.

Adds the CAT Ability Trajectory report (Tier 2), the CPE Report (Tier 4), a comprehensive theme system (12 named palettes on all four surfaces), three light-theme bug fixes, a SQLite crash fix, SHA-256 manifest verification, Android safe-area inset and tap-delay fixes, dropdown readability improvements, and updates the theme count to 13 palettes with the addition of this release's new colour schemes.

Fix — Theme selection had no visible effect (PC only)

Examulator · Examulator3

Selecting any theme (Light, Sepia, Paper, or the dark variants) under Accessibility → Visual Theme saved correctly but produced no visible change on the PC/web surface. Root cause: the base stylesheet set background-color and color on html, body together, reading --bg-root / --text-primary from :root. The theme attribute is emitted on <body>, so a theme's variable overrides reached body and its descendants but never html (body's parent) — the html element kept painting the default dark canvas over the themed body. Fixed by scoping the themed background-color/color to body alone and making html transparent, so the body-level theme override is the sole canvas colour. MAUI was unaffected (its html/body rules were already separated).

Feature — CAT Ability Trajectory (Tier 2)

ExamulatorCore · Examulator3Core · Examulator · Examulator3 · ExamulatorMaui · Examulator3Maui

New Tier 2 report that plots the CAT algorithm's ability estimate (theta, scaled 0–1000) and 95% confidence interval question-by-question across a single exam session. The 700 passing standard is marked with a dashed line; red/yellow/green zone shading shows when the estimate is below, at, or above the threshold. SE is computed from cumulative item information when IRT parameters are available; a proportional approximation (se_final × √(N/q)) is used otherwise. Registered as ReportKind.CatAbilityTrajectory = 10; CAT-only, single-session.

Feature — CPE Report (Tier 4)

ExamulatorCore · Examulator3Core · Examulator · Examulator3 · ExamulatorMaui · Examulator3Maui

New Tier 4 report that generates a formal PDF certificate suitable for CPE credit submissions to ISC², ISACA, or similar bodies. The user selects sessions for one exam and enters the recipient name; the report tallies total study hours and minutes, prints a certificate block, and appends a per-session detail table (start/end date-times, mode, question count, duration). ReportPdfExportService.BuildCpeAsync bypasses the generic BuildAsync dispatch to carry the recipient name. All four surfaces implemented.

Feature — Theme system (12 named palettes)

Examulator · Examulator3 · ExamulatorMaui · Examulator3Maui

Twelve named colour palettes added to the Accessibility settings dropdown on all four surfaces: Midnight (OLED black), Slate (cool gray), Warm Dark (charcoal), Nord (arctic blue, frost-teal accent), Solarized Dark (teal bg, yellow accent), Amethyst (purple accent), Catppuccin Mocha (mauve accent), Green Terminal (phosphor green on black), Light (cream), Sepia (warm cream), Paper (pure white), and High Contrast (existing). Each palette overrides all relevant CSS custom properties, including tokens that were missing from the original light theme — --bg-root, --bg-nav, --text on MAUI, and all six gold derivative rgba values — so none of the readability failures that affected the original light theme can recur.

Fix — Light theme readability (3 bugs)

Examulator · Examulator3 · ExamulatorMaui · Examulator3Maui

Three defects in the existing light accessibility theme corrected across all four CSS files: (1) --bg-root (#07111f dark navy) and --bg-nav (#050e1a near-black) were never overridden — the page canvas and nav bar stayed dark while text flipped to #1a1a1a, producing dark text on a dark background; (2) --gold-subtle, --gold-border, and --gold-glow were hardcoded as rgba(201,162,39,...) dark-mode values and not overridden, affecting 48 CSS rules; (3) MAUI --text (#C8C8C8 light-gray) was not overridden, producing near-invisible text on the cream background.

Fix — Dropdown readability

Examulator · Examulator3 · ExamulatorMaui · Examulator3Maui

<select option> elements take their background and color from the OS UA stylesheet and ignore styles applied to the parent <select>. Added explicit background-color and color overrides with !important on all four CSS files so the app theme is honoured inside dropdown lists in every theme including light and sepia.

Fix — SQLite crash in CountForQuizAsync

ExamulatorCore · Examulator3Core

QuestionRepository.CountForQuizAsync had a missing closing ) on its dynamically-built NOT IN clause — the generated SQL was WHERE questionGuid NOT IN (@ex0, @ex1 with no closing paren, which SQLite correctly reports as SQLite Error 1: incomplete input. Affected all platforms when the quiz config page loaded with any non-empty exclude set. Fixed in both Gen2 and Gen3.

Feature — SHA-256 manifest verification

ExamulatorCore · ExamulatorUpdater · Examulator · Examulator3

The version manifest now supports an optional third whitespace-separated field per line: a 64-character hex SHA-256 digest of the download ZIP. Backward compatible — old clients see only the first two fields and ignore the third. When a hash is present, ExamulatorUpdater computes SHA256.HashDataAsync on the downloaded file after completing the download and aborts with exit code 4 on mismatch. Hash propagated through UpdateCheckResult.AppDownloadSha256 and the --sha256 CLI argument. AllDatabaseHashes dictionary added to UpdateCheckResult for future DB-download verification.

Fix — Android safe-area inset (status bar overlap)

ExamulatorMaui · Examulator3Maui

.top-bar had a fixed height: 52px while padding-top: env(safe-area-inset-top) consumed that budget on Android 15+ edge-to-edge devices (Pixel 10 Pro Fold and others), leaving the brand/nav buttons rendered behind the system clock. Fixed by changing to height: calc(52px + env(safe-area-inset-top, 0px)) so the bar expands rather than compresses. Both MAUI surfaces updated.

Fix — Android 300 ms tap delay

ExamulatorMaui · Examulator3Maui

touch-action: manipulation was scoped to .btn only, leaving all other interactive elements subject to the double-tap delay (manifesting as "must click multiple times" on foldable devices). Broadened to a, button, input, select, textarea, label, [role="button"], [role="tab"], [role="menuitem"] on both MAUI surfaces.

Feature — iOS platform support

ExamulatorMaui · Examulator3Maui

net10.0-ios added to TargetFrameworks in both MAUI csproj files. Platforms/iOS/ boilerplate added to both Gen2 and Gen3: AppDelegate.cs, Program.cs, Info.plist (minimal — MAUI merges bundle ID, version, and icon from csproj). publish-ipa.ps1 added alongside the existing publish-aab.ps1. <PackageVersion>1.0.0</PackageVersion> pinned in both MAUI csproj files to resolve NU1105 (iOS SDK sets PackageVersion from ApplicationDisplayVersion during NuGet restore; Android leaves it at the default, causing a mismatch).

Fix — Android adaptive icon

ExamulatorMaui · Examulator3Maui

android:roundIcon="@mipmap/appicon_round" removed from both Gen2 and Gen3 AndroidManifest.xml. MAUI does not generate appicon_round mipmap resources; launchers that specifically requested the round icon variant resolved to a missing resource and fell back to the generic Android system icon on some devices (notably Pixel launchers on Android 7.1).

Infrastructure — Version bumped to 26258.02-rc1 (12 sites)

Examulator · ExamulatorMaui · Examulator3 · Examulator3Maui · ExamulatorDiag · Examulator3Diag · ExamulatorTrace · Examulator3Trace

Daily revision bump .01 → .02 on Julian day 26258 (2026-09-15) for the PC theme-selection fix above. Channel unchanged (rc1). MAUI <ApplicationVersion> = 2625802 (26258 × 100 + 2).


[26247.01-rc1] — 2026-09-04

Scope: Both generations, all four surfaces.

Infrastructure and patch release. Julian date bump, Android icon fix, and iOS platform scaffolding.

Infrastructure — Version bumped to 26247.01-rc1 (12 sites)

Examulator · ExamulatorMaui · Examulator3 · Examulator3Maui · ExamulatorDiag · Examulator3Diag · ExamulatorTrace · Examulator3Trace

Julian date bump from 26232 to 26247 (2026-09-04). Channel unchanged (rc1). MAUI <ApplicationVersion> = 2624701 (26247 × 100 + 1). Android icon fix and iOS platform support included (see 26258.01-rc1 for full detail).


[26232.01-rc1] — 2026-08-20

Scope: Both generations, all four surfaces.

Adds a new Raw Question Review report (Tier 4 — Study Tools) and clears the compile-time breakage that surfaced after building 26228.01 against a fresh checkout. All changes mirrored across both product generations and all four surfaces (PC Gen2, PC Gen3, MAUI Gen2, MAUI Gen3).

Feature — Raw Question Review (Tier 4 — Study Tools)

ExamulatorCore · Examulator3Core · Examulator · Examulator3 · ExamulatorMaui · Examulator3Maui

A study-focused report — the first entry in a new Tier 4 "Study Tools" catalog band — that walks the user through questions across multiple sessions in the same detailed layout as the exam-result question review. Not an analysis: no charts, no scores, no aggregates. Purpose is re-drilling weak spots after several practice sessions have accumulated.

Fix — Razor tag-helper attribute conflict on <body> (_Layout.cshtml)

Examulator · Examulator3

.NET 10 treats <body> as a tag-helper element and rejects inline @(...) expressions in its attribute declaration area (RZ1031). The accessibility layer was emitting seven conditional data-a11y-* attributes with per-attribute expressions of the form @(!string.IsNullOrEmpty(x) ? $"data-a11y-x=\"{x}\"" : ""), which failed to compile.

Initial workaround concatenated the attributes in the top-level @{ } block and emitted them via <body@Html.Raw(bodyAttrs)>, which cleared RZ1031 but left the tag-helper parser unable to recognize the <body> start tag, so the matching </body> at file end triggered RZ1034 ("malformed body tag helper").

Correct fix: idiomatic ASP.NET tag-helper null-attribute pattern — give each attribute a proper name="@(cond ? value : null)" form. When the expression evaluates to null, the tag helper omits the attribute entirely (no empty data-a11y-theme="" rendered), no C# sits in the attribute-declaration area, and the tag remains well-formed. Also silences the four spurious CS0219 "assigned but never used" warnings on the a11yComfort / Motion / Focus / Timer locals — they were used, but the failed body-tag parse had hidden that from the analyzer.

Documentation — @ReadMe.md fully rewritten

@ReadMe.md

The distribution README had drifted badly since v26195.02-alpha (May 2026) — most of the document described a solution structure and roadmap that had been superseded by four to six months of shipped work. Rewritten from scratch to reflect the current product.

User-facing sections rewritten: - Supported Certifications — all nine ISC² certifications marked as Available (previously CC / CCSP / CGRC / CISSP / CSSLP available with SSCP / ISSAP / ISSEP / ISSMP as coming soon). Added a Native delivery mode column (CAT vs Linear per Pearson VUE) alongside Examulator's supported modes for each certification. New ISACA (via Examulator3 — in development) subsection. - Multi-platform framing — front matter now leads with "desktop/web AND Android"; previously implied desktop-only. Android installation subsection added with both Play Store package IDs (com.certificationtoolanddie.examulator for Gen2, com.certificationtoolanddie.examulator3 for Gen3). - Features — Reports subsystem added with all four tiers listed (Core / Advanced / Deep Dive / Study Tools including Raw Question Review); Accessibility program summarized (WCAG 2.1 AA target, ten settings, screen-reader chart tables, extended time, flag notes); Contribute Results named correctly (previously "Anonymous data sharing") and cross-referenced to Privacy-Policy.md §5.3; Diagnostic export mentioned. New Gen3 additions subsection covers per-answer explanations, key references, and the multi-agency schema. - Configuration table — verified against current appsettings.json; kept.

Developer-facing sections rewritten: - Solution Structure — was 6 projects (Examulator / ExamulatorCore / ExamulatorIrt / ExamulatorDiag / ExamulatorTrace / ExamulatorApi / ExamulatorTests), now correctly shows the current 18 projects grouped by Shared / Gen2 / Gen3. Root-namespace convention explained. - Tech Stack — added .NET MAUI Blazor Hybrid, MigraDoc/PdfSharp with the bundled Roboto font, Chart.js (both reportCharts.js and reportChartsMaui.js), and updated framework version. - Build Requirements — added MAUI workloads (dotnet workload install maui-android) and the AndroidTargetSdkVersion 36 Play Store deadline (Aug 31 2026). - Local Development — new note that Directory.Packages.props is the source of truth for NuGet versions and that per-project PackageReference versions must not be edited. - Data Model — new short section describing the exam DB vs Results.db split and the per-generation Results.db isolation. - Release Process — new short section explaining the YYDDD.VV-channel version format.

Sections removed: - Phase 2 Migration Notes (for developers merging v26195.02-alpha) — historical migration guidance for a release cycle four+ months in the past; no longer relevant. - MAUI Android Port (in progress) — MAUI ships as both Gen2 and Gen3 apps; the "in progress" framing was long stale. - Contact section expanded from a single mike@ address to all five published addresses (feedback@, contribute@, accessibility@, privacy@, mike@).

Infrastructure — Version bumped to 26232.01-rc1 (12 sites)

Examulator · ExamulatorMaui · Examulator3 · Examulator3Maui · ExamulatorDiag · Examulator3Diag · ExamulatorTrace · Examulator3Trace

Suffix changed from -closed to -rc1 for Google Play Early Access (open testing) submission. Standard 12-site bump. MAUI <ApplicationVersion> = 2623201 (26232 × 100 + 1). No display-format changes.

Infrastructure — Version bumped to 26231.01-closed (12 sites)

Examulator · ExamulatorMaui · Examulator3 · Examulator3Maui · ExamulatorDiag · Examulator3Diag · ExamulatorTrace · Examulator3Trace

Standard 12-site bump. MAUI <ApplicationVersion> = 2623101 (26231 × 100 + 1). No display-format changes.


[26228.01-closed] — 2026-08-16

Scope: Both generations, all four surfaces.

Major milestone release, versioned for Play Store closed-beta submission. Headline addition is the opt-in Contribute Results feature that anonymously transmits IRT calibration data to help refine question item parameters. Also lands a comprehensive accessibility program targeting WCAG 2.1 AA (settings, screen-reader chart tables, extended-time accommodation, flag notes — all four surfaces), the Exam Reports subsystem (three tiers of multi-session analysis), per-answer explanations and key references on Gen3, per-session kebab actions on the Single Exam Report configure screen (all four surfaces), MAUI action-sheet kebab pattern, collapsible Help sections on the web, two-column splash layout, build-timestamp via AssemblyMetadataAttribute reflection, returnUrl propagation for back-navigation, CSS contrast improvements across the web app, and a substantial bug-fix pass.

Feature — Contribute Results (opt-in IRT calibration data)

Examulator · ExamulatorCore · ExamulatorMaui · Examulator3 · Examulator3Core · Examulator3Maui · ExamulatorApi

Optional, opt-in feature that lets users transmit an individual exam result to a central calibration API to help refine question item parameters (discrimination, difficulty, guessing). Disabled by default; requires the user to request an API URL from contribute@certificationtoolanddie.com and paste it into a Setup screen.

Feature — Accessibility program (WCAG 2.1 AA target)

Examulator · ExamulatorCore · ExamulatorMaui · Examulator3 · Examulator3Core · Examulator3Maui

Comprehensive accessibility push spanning screen-reader support (Phase 1), low-vision and motion accommodations (Phase 2), cognitive aids (Phase 3), and motor accommodations (Phase 4). All four surfaces. Companion public statement in Accessibility-Statement.md.

Feature — Per-answer explanations & key references (Gen3-only)

Examulator3 · Examulator3Maui · Examulator3Core

The Gen3 exam schema carries a rationale on every answer choice — a full explanation for the correct answer and a distractor rationale for each wrong one — plus a per-question key reference (a further-reading citation). This release completes the UI rendering:

Gen2 is unchanged — it uses a single question-level explanation model.

Feature — Diagnostic export schema v3 (Gen3-only)

Examulator3Diagnostics · Examulator3Trace

DiagDoc.KnownSchemaVersion bumped "2" → "3". New fields on QuestionDetail:

DiagDocBuilder, SessionSnapshot, and DiagnosticExportService updated to populate and map the new fields. Gen2 stays at schema "2".

Hardening — Diagnostic export schema guard

ExamulatorTrace · Examulator3Trace

Both Trace tools now check a loaded diagnostic file's schemaVersion before rendering it, refusing (with a clear message) any document produced by a newer schema. A blank/absent version is treated as legacy v1 and still accepted. KnownSchemaVersion must be bumped in lockstep between the Diagnostics library and the Trace reader whenever the DiagDoc shape gains fields.

Feature — Exam Reports (three tiers of multi-session analysis)

Examulator · ExamulatorCore · ExamulatorMaui · Examulator3 · Examulator3Core · Examulator3Maui

A new Reports entry (📊, replacing the old Results nav link on MAUI) opens a report catalog; each report leads to a configuration screen where the user picks an exam and a set of sessions, then to a generated report with interactive charts.

Tier 1 — Single Exam Report: the classic single-session detail view, reached through the Reports catalog with the same interactions as Exam History.

Tier 2 — Domain Trends: per-domain scaled score over the selected sessions, with proficiency threshold overlay and colour-coded pass/fail bands.

Tier 3 — Comprehensive Analysis: aggregate summary combining domain trends, difficulty distribution, and time-per-question analysis across the selected sessions.

The ReportPdfExportService (both Core projects) produces a printable PDF matching the on-screen report. DomainTrendsViewModel.Series (was .Domains) fix applied when the per-domain aggregator was refactored.

Feature — Multi-session PDF export

ExamulatorCore · Examulator3Core

ReportPdfExportService renders reports covering many sessions to a single PDF using empira PdfSharp/MigraDoc. Roboto font is bundled so output is consistent across host platforms. Called from the Reports Output page on both PC and MAUI.

Feature — Single Exam Report row kebab (all four surfaces)

Examulator · ExamulatorMaui · Examulator3 · Examulator3Maui

On the Single Exam Report configure screen the multi-select checkbox list is replaced with clickable rows in the history-item visual style used by Exam History. Tapping a row navigates directly to that session's Historical Results view — no separate "Generate Report" step is needed for a single-selection report. A per-row ⋮ kebab exposes the same actions available from the Exam History kebab: View, Export PDF, Delete (with confirmation), and Export Diagnostic. Multi-session reports retain the checkbox + Generate Report pattern unchanged.

The Last 5 / Last 10 / All quick-pick buttons and the "N selected" counter are hidden on the Single Exam Report screen since the report by definition requires exactly one session.

Feature — Return URL for HistoricalResults back button

Examulator · ExamulatorMaui · Examulator3 · Examulator3Maui

Both PC page models gained a [FromQuery] ReturnUrl parameter (default /ExamHistory) and both MAUI HistoricalResults gained [SupplyParameterFromQuery] ReturnUrl. All back buttons and post-delete navigation now respect the ReturnUrl query parameter. Callers from the Single Exam Report configure screen pass returnUrl=/Reports/Configure/0, so Back returns to the report selection screen rather than defaulting to Exam History. Callers from Exam History omit the parameter and get the previous behaviour.

Feature — Build timestamp via reflection

Examulator · ExamulatorMaui · Examulator3 · Examulator3Maui

Splash screens now show "Built {timestamp} UTC" beneath the version stamp. The timestamp is embedded in each entry-point assembly at compile time via an MSBuild AssemblyMetadata item, then read back at runtime via Assembly.GetCustomAttributes(typeof(AssemblyMetadataAttribute), false). Works on Android where Assembly.Location is empty because the attribute is embedded in the binary metadata, not resolved from disk. No appsettings.json maintenance required.

Feature — Collapsible Help sections (web)

Examulator · Examulator3

All sections on the Help page now render collapsed by default with only the numbered title row visible. Clicking any header expands or collapses that section. A ▼ chevron rotates 90° when collapsed. Implementation is CSS-only display toggling driven by a three-line querySelectorAll listener — no framework overhead.

Feature — Web splash — 2-column layout

Examulator · Examulator3

The splash screen now uses a two-column layout at viewports ≥820px: logo on the left, Important Notice card + footer stacked vertically on the right, vertically centred against the logo's height and horizontally centred within the right column. Mobile viewports fall back to the original single-column stack.

Feature — Reports catalog entry for Exam History

Examulator · ExamulatorMaui · Examulator3 · Examulator3Maui

"View Exam History" now renders as a full-width report-card entry in the Reports catalog, matching the width and visual style of the tier cards above it. On MAUI the 📋 Exam History icon has been removed from the splash screen and the top navigation bar — all history access flows through the Reports catalog.

Enhancement — MAUI Historical Results kebab

Examulator3Maui · ExamulatorMaui

HistoricalResults (the detail view for one session) gained its own ⋮ kebab in the header with Export PDF, Delete (with red confirmation bar), and Export diagnostic actions. Injections and delete-navigation identical to the Exam History kebab pattern.

Enhancement — MAUI Historical Results — difficulty by level collapsible

Examulator3Maui · ExamulatorMaui

The "By Difficulty Level — all domains" section moved from above the per-domain breakdown to below it, immediately before the Question Review divider. It is now collapsed by default with a ▼/▲ toggle in the same visual style as the Question Review toggle. Bottom bar "← Back to History" and "← History" labels simplified to "← Back".

Enhancement — Web quiz options readability & contrast

Examulator · Examulator3

Two CSS-variable-level contrast fixes:

Downstream effects (no markup changes needed): the "All" chip when no filter is applied, Time Remaining / Question # labels in the exam header, the "How this score was computed" title on results, metadata lines throughout the app, and the review objective strip all pick up the improved contrast automatically. .fc-label in Quiz Config (Difficulty, Domains, Question Pool labels) upgraded from --text-dim to --text-muted. The "10 (minimum)" and "N (full bank)" hint spans under the Question Count slider changed from text-dim to text-muted with the font size bumped from .78rem to .8rem.

Enhancement — Question triband icons removed (web)

Examulator · Examulator3

The clock icon next to "Time Remaining" and the question-mark icon next to "Question #" in the exam header removed; each stat block now goes directly label → value. The label row wrappers also removed since they only existed to hold the icons.

Fix — QuizConfig agency route regression (Gen3)

Examulator3 · Examulator3Maui

Gen3 QuizConfig was calling GetExamByCodeAsync(examCode) and GetDomainsAsync(examCode) with a single argument, but the Gen3 repository signatures require (agency, examCode). PC route changed from {code} to {agency}/{code}; MAUI route changed from /QuizConfig/{Code} to /QuizConfig/{Agency}/{Code}; both page models gained an Agency parameter and all repository calls updated. Missing using for ExamSession namespace added to both QuizConfig.cshtml.cs files.

Fix — MAUI Gen2 Configure agency drift (regression)

ExamulatorMaui

The Gen2 MAUI Configure page was accidentally carrying Gen3-specific AgencyCode references (exam.AgencyCode, dropdown value {agency}|{examCode}, filter comparing both fields). Reverted to the Gen2 signatures: dropdown value is exam.ExamCode alone, filter matches on ExamCode only, GetSessionsAsync called with the 3-arg Gen2 signature.

Fix — Razor @{ } regression in .NET 10

Examulator · Examulator3

The .NET 10 Razor compiler rejects @{ var x = ...; } code blocks placed between HTML elements inside an @if { } else { } body (RZ1010). Fixed by moving affected variable declarations (notes in Reports/Output.cshtml, reviewWrongExpl in Exam/Question.cshtml) to the top-level @{ } block at the head of each page.

Fix — Razor tag-helper attribute conflict on <body> (_Layout.cshtml)

Examulator · Examulator3

.NET 10 Razor treats <body> as a tag-helper element and rejects inline @(...) expressions in its attribute declaration area (RZ1031). The accessibility layer was emitting seven conditional data-a11y-* attributes with per-attribute @(!string.IsNullOrEmpty(x) ? $"data-a11y-x=\"{x}\"" : "") expressions, which failed to compile. Fixed by building the attribute string in the top-level @{ } block via StringBuilder and emitting it with @Html.Raw(bodyAttrs) on the <body> tag. This also silences the CS0219 "assigned but never used" warnings for the seven a11y* locals (they were used, but the failed body-attr parse hid that from the compiler).

Fix — DiagErrorBoundary ChildContent naming conflict

Examulator3Maui · ExamulatorMaui

In .NET 10 Blazor, using <ChildContent>@ChildContent</ChildContent> as an explicit slot inside <ErrorBoundary> when the outer component also declares [Parameter] public RenderFragment? ChildContent causes the source generator to emit a conflicting local variable (CS0841 / CS7014 cascade). Fixed with a private accessor _body => ChildContent referenced in the template. Also fixed literal newlines inside '\n' and "\n" char/string literals in the TrimStack method (CS1010 / RZ1000 / RZ1027 errors) with proper \n escape sequences.

Fix — ReportPdfExportService DomainTrends series name

ExamulatorCore · Examulator3Core

RenderDomainTrends iterated vm.Domains but DomainTrendsViewModel exposes per-domain data as Series (List<DomainTrendSeries>). Fixed in both Core projects.

Fix — SessionQuestion.Explanation → CorrectExplanation in Results review

Examulator3

Results.cshtml was referencing the non-existent q.Explanation in the legacy fallback block. Changed to q.CorrectExplanation (the computed property that returns the correct answer's explanation from the answers list).

Fix — File(inline: true) overload does not exist

Examulator3

PageModel.File() has no inline named parameter. Dropped to the two-argument overload File(pdf, "application/pdf") in HistoricalResults.OnPostExportPdfAsync — the browser displays inline by default when no Content-Disposition: attachment header is present.

Fix — Nullable analyser q.Answers in MAUI Question review

Examulator3Maui

Added a (q.Answers ?? []) fallback in the displayWrongExpl computation to satisfy the nullable analyser (CS8604), even though HistoricalQuestion.Answers is initialised to new().

Infrastructure — Version bumped to 26228.01-closed (12 sites)

Examulator · ExamulatorMaui · ExamulatorDiag · ExamulatorTrace · Examulator3 · Examulator3Maui · Examulator3Diag · Examulator3Trace

12 sites updated in lockstep: 4 appsettings.json (Version + Channel), 2 MauiProgram.cs fallback line pairs, 2 Diag/Program.cs (Version + VersionNumber const strings), 2 Trace/TraceInfo.cs (Version const), 2 MAUI .csproj (ApplicationVersion code = 2622801, ApplicationDisplayVersion = 26228.01-closed). Gen3 metadata (appsettings + csproj) advanced from placeholder 0.0.1-dev to full alignment with Gen2's release number for the first time.

Documentation — Privacy Policy expanded for Contribute Results

Section 5 gained a comprehensive new subsection describing the Contribute Results feature in full — how it is enabled, the exact fields transmitted, what is explicitly excluded, how the data is used, and how to disable or delete contributed data. Section 10 gained API-key-based deletion instructions. Sections 8, 9, 11, and 13 updated to reference the feature where relevant. Privacy-Policy.html now included at the root of the source zip alongside @ReadMe.md and both CHANGELOG-*.html files.


[26226.01-closed] — 2026-08-14

Scope: Both generations, all four surfaces.

Feature-heavy release: diagnostic-export workflow with a new shared library, kebab menu on the Exam History screen, print-preview page, and a click-the-exam-name Abort Exam mechanism. Also includes browser back-button protection on PC and a fix for a beforeunload false-positive that only surfaced in timed-quiz mode.

Feature — Diagnostic export workflow

The standalone ExamulatorDiag tool's DiagDoc-generation logic is now extracted into a shared library that both the standalone tool and the running application consume. Users can export a diagnostic JSON directly from the Exam History screen without needing to run the desktop tool.

ExamulatorDiagnostics (new class library, both generations)

ExamulatorDiag standalone tool refactored

DiagnosticExportService (new, in ExamulatorCore + Examulator3Core)

Feature — Exam History kebab menu

Each row on the Exam History screen gets a ⋮ button on the right, opening a four-item floating menu:

Row-tap behavior unchanged — still opens Results detail directly. Consistent affordance across PC and MAUI on both generations.

Feature — Print preview page

New page at /Exam/PrintPreview?sessionGuid=... for all four surfaces:

Feature — Abort Exam

Discoverable way to abandon an in-progress exam without saving results, on all four surfaces.

Feature — Diag --version prints numeric-only

The standalone ExamulatorDiag --version and Examulator3Diag --version flags now print just the numeric portion (e.g. 26226.01) without the channel suffix. Build scripts embedding the version in a ZIP filename get a clean value without needing to strip the suffix themselves. The full display version continues to be used in the banner and embedded in DiagDoc exports.

Fix — Browser back button / tab close on PC

Prior to this release, hitting the browser's Back button, closing the tab, or refreshing the page during an exam silently abandoned the session with no confirmation.

MAUI (Android) hardware back button was already handled correctly via the existing LocationChangingHandler.

Fix — beforeunload guard false-positive on Next button (PC, timed exams)

Reported by user: "User does quiz, unchecks the Untimed Quiz option. Gets to question 1, answers, clicks Next and gets a browser warning 'Leave site? Changes will not be saved'."

Root cause: HTMLFormElement.submit() called programmatically does NOT fire the form's submit event. Only user-initiated submissions (clicking a <button type="submit">, pressing Enter in a text input) fire that event. This is a well-documented HTML/DOM quirk.

The timed-exam Next button uses <button type="button" onclick="submitAnswer()"> because it needs JS validation before submitting. submitAnswer() calls document.getElementById('answerForm').submit() directly — bypassing the submit event entirely. The _allowUnload bypass flag was being set by a form.addEventListener('submit', ...) listener that never fired, so beforeunload correctly saw an "unarmed" navigation and prompted.

The untimed-non-quiz path used <button type="submit"> on the answer form which naturally fires the submit event, so that path worked fine — which is why the bug only surfaced when the Untimed Quiz option was disabled.

Fix:

Infrastructure — Per-project publish scripts removed

Every code project had a publish.cmd / publish.sh pair used for early ad-hoc publish scripting. Superseded by the top-level publish-aab.ps1 workflow (for MAUI Play Store submissions) and standard dotnet publish invocations for the other projects, so the per-project scripts were adding clutter without adding value. All 16 have been deleted (8 Gen2 + 8 Gen3 projects).

Infrastructure — Gen3 project rename to Examulator3{Module} convention

All Gen3 projects moved from the legacy ExamulatorX3 suffix form to the consistent Examulator3X prefix form:

Old name New name ---------------— ------------------ ExamulatorCore3 Examulator3Core ExamulatorMaui3 Examulator3Maui ExamulatorDiag3 Examulator3Diag ExamulatorApi3 Examulator3Api ExamulatorTests3 Examulator3Tests ExamulatorTrace3 Examulator3Trace

Each rename touched:

Zero legacy references remaining anywhere in the Gen3 tree after the rename.


[26223.02-closed] — 2026-08-11

Scope: Both generations, all four surfaces.

Consolidation release. Beta-line bug fixes, closed-testing track preparation, and copy corrections.

Fix — Abandon Exam button unresponsive (Android)

ExamulatorMaui · Examulator3Maui

LocationChangingHandler was firing recursively on confirm-abort. Fixed by adding an _exitConfirmed flag that short-circuits the handler on user-initiated abort.

Feature — Splash-screen version stamp

Examulator · ExamulatorMaui · Examulator3 · Examulator3Maui

PC and MAUI splash screens render Version {version} read at runtime from App:Version + App:Channel. Gracefully absent if keys are missing.

Fix — Data-sharing disclaimer copy

Examulator · ExamulatorMaui · Examulator3 · Examulator3Maui

Help, About, and FAQ updated to lead with "does not share any information" and mark the future IRT-upload feature as "Under development — not currently active".

Fix — ISC² CAT overclaims removed

Examulator · ExamulatorMaui · Examulator3 · Examulator3Maui

Help copy now claims only the class of technique without asserting identity with ISC²'s operational system. Linear mode card no longer describes itself as "paper-based".


[26222.10-alpha] — 2026-08-10 (Gen3-only)

Scope: Examulator3 · Examulator3Maui · Examulator3Core (Gen3-only).

Examulator3 · Examulator3Core · Examulator3Maui

Foundational Gen3 agency schema, repository signature refactor, and download-hang fix ported from Gen2. New Agencies table; Exams.agencyGuid FK; AgencyRepository; agency-tab UI on SelectExam. File-naming conventions locked. Every per-exam method in Examulator3Core now takes (agency, examCode).


[26222.02-alpha] — 2026-08-10 (Gen3 fork established)

Scope: Gen3 fork established — Examulator3 · Examulator3Core · Examulator3Maui · Examulator3Diag · Examulator3Trace · Examulator3Api · Examulator3Tests · Examulator3Diagnostics.

Examulator3 · Examulator3Core · Examulator3Maui · Examulator3Diag · Examulator3Trace · Examulator3Api · Examulator3Tests

Gen3 forked from Gen2 checkpoint 26221.17-alpha. Namespaces renamed (Examulator.* → Examulator3.*); fresh ApplicationId and ApplicationTitle (Examulator3). Schema additions: Question.KeyReference, Answer.Explanation, AnswerChoice.Explanation, Exam.AgencyCode, Exam.AgencyGuid. Removed: Question.Explanation (replaced by per-answer explanations on the correct choice). DatabaseFactory takes (agency, examCode) on every per-exam method.


[26221.17-alpha] — 2026-08-09 (Gen2 checkpoint — immutable pre-Gen3 baseline)

Scope: Examulator · ExamulatorCore · ExamulatorMaui and supporting projects — immutable Gen2 checkpoint; Gen3 projects did not yet exist.

Examulator · ExamulatorCore · ExamulatorMaui

Snapshot taken immediately before the Gen3 fork. Preserved as the immutable reference baseline; always included in handoff zips. All entries below are Gen2 only.


[26220.06-alpha] — 2026-08-08

Scope: Examulator · ExamulatorCore · ExamulatorMaui (Gen2 only — pre-Gen3 fork).

Examulator · ExamulatorCore · ExamulatorMaui

Closed-testing push. Quiz History filter corrected for mixed Unanswered + Answered selections. Central Package Management adopted (Directory.Packages.props); CentralPackageTransitivePinningEnabled = false; CentralPackageFloatingVersionsEnabled = true.


[26216.02-alpha] — 2026-08-04

Scope: Examulator · ExamulatorCore · ExamulatorMaui (Gen2 only — pre-Gen3 fork).

Examulator · ExamulatorCore · ExamulatorMaui

Quiz Config difficulty filter chips — restrict the question pool to selected difficulty bands. Question history filters — Unanswered Only / Include Incorrect / Include Correct / Include Marked-for-Review; shared BuildHistoryFilterAsync service. MAUI question-count slider — replaces numeric input, steps by 5.


[26216.01-alpha] — 2026-08-04

Scope: Examulator · ExamulatorCore · ExamulatorMaui (Gen2 only — pre-Gen3 fork).

Examulator · ExamulatorCore · ExamulatorMaui

Exam History detail page — tapping a row opens paginated question-by-question review with answers and explanations. MAUI session-persistence checkpoint — written on every answer submit, restored on cold-start if the user was mid-exam.


[26182.01-alpha] — 2026-07-01

Scope: Examulator · ExamulatorCore · ExamulatorMaui (Gen2 only — pre-Gen3 fork).

Examulator · ExamulatorCore · ExamulatorMaui · ExamulatorIrt

IRT-powered CAT mode — full 3PL IRT: Newton–Raphson MLE theta, maximum-information item selection, three-rule termination (CI boundary, max questions, time), scaled score anchored at θ = 0 → 700. Linear exam mode — stratified random selection across domains weighted by published ISC² percentages, difficulty-balanced allocation per domain.


[26167.01-alpha] — 2026-06-16

Scope: Examulator · ExamulatorCore · ExamulatorMaui (Gen2 only — pre-Gen3 fork).

Examulator · ExamulatorMaui · ExamulatorCore

Quiz mode — untimed (or timed) with immediate answer reveal, Mark for Review, and Feedback flags. Back-navigation through answered questions. Database download and installation — Google Drive–hosted exam DB ZIP download, extraction, and placement on both PC and MAUI. Progress UI on both surfaces.


[26160.01-alpha] — 2026-06-09

Scope: Examulator · ExamulatorCore (Gen2 only — pre-Gen3 fork; MAUI port not yet in tree).

ExamulatorMaui

MAUI Blazor Hybrid app scaffolded — net10.0-android, shared Razor components, IDataPathProvider abstraction. autostart="false" on blazor.webview.js required to prevent Blazor starting before the MAUI native handler is ready.


[26155.01-alpha] — 2026-06-04 (Initial alpha release)

Scope: Examulator (Gen2 only — initial alpha; single-project solution).

Examulator · ExamulatorCore · ExamulatorIrt

PC web application — ASP.NET Core 10 Razor Pages. SelectExam, SelectMode, Question (CAT / Linear / Quiz), Results, Exam History. ExamulatorCore — SQLite via SourceGear.sqlite3, DatabaseFactory, repository layer. ExamulatorIrt — 3PL IRT math: raw-to-float conversion, P(θ), Newton–Raphson MLE, maximum-information selection. Shared by PC, MAUI, Diag, and unit tests.