Windows-first .NET MAUI desktop foundation for a local Magic: The Gathering workspace. The shell provides a local deck library, AI providers and privacy, and About navigation. Create a named entry, then select its name to open its workbench. Save Commander or Legacy metadata, strategy and constraint notes, Commander identity, and optional sideboard notes. The history picker opens any saved version; saving edits appends a new version. Pasted decklist parsing and storage are available at the application boundary. The paste UI, public import transport, card editing, evaluation, and playtesting belong to later tickets.
Use Windows 10 version 1809 or later (Windows 11 recommended), an x64 development
machine, .NET SDK 10.0.301 (or a later 10.0.3xx patch), and the maui-windows
workload. Install the .NET MAUI development tools through Visual Studio, or install
the workload from an elevated terminal:
dotnet workload install maui-windowsThe Windows SDK 10.0.19041.0 or newer must be installed with the MAUI development tools. The first restore needs NuGet access. From the repository root:
dotnet restore MagicJev.slnx
dotnet build MagicJev.slnx --no-restore
dotnet test tests/MagicJev.Application.Tests/MagicJev.Application.Tests.csproj --no-build
dotnet run --project src/MagicJev.Desktop/MagicJev.Desktop.csproj --no-buildThe app is unpackaged, so local launch requires no signing certificate or MSIX installation. Windows App SDK runtime files are included in build output. The .NET runtime is supplied by the development SDK. macOS is not a configured or validated target yet.
Run from an interactive Windows desktop after building. The script launches the actual application, verifies its initial page, navigates to About and back twice using Windows UI Automation, and closes only the process it started:
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/smoke-windows.ps1For a manual check, launch with dotnet run, verify the Workbench opens, select
About in the navigation rail, and return with Workbench. Also exercise both page
buttons and resize the window. The page text must remain readable and scrollable.
-
src/MagicJev.Desktop: MAUI presentation and composition root (MauiProgram).Platforms/Windowsexclusively owns native Windows startup and manifests. -
src/MagicJev.Application: plainnet10.0application boundary and stable shell routes. References Domain; has no MAUI or Windows dependencies. -
src/MagicJev.Domain: plainnet10.0domain boundary with provider configuration values and validation. Has no project or package dependencies. -
tests/MagicJev.Application.Tests: executable xUnit harness for application seams; requires neither MAUI nor a desktop session when run independently. -
src/MagicJev.Storage: plainnet10.0JSON file adapter implementing the application storage interface; its data directory is supplied by the host.
Dependency direction is Desktop -> Storage -> Application -> Domain, with Desktop also referencing Application. These non-desktop assemblies have no MAUI dependency. The application performs no provider calls, telemetry, or background synchronization.
The shell saves its selected library/settings/About page, provider configuration, deck entries, and immutable deck snapshots to version 5 of application-state.json in
MAUI's FileSystem.Current.AppDataDirectory, and restores it on the next launch.
This is the normal per-user application data location; it is not the checkout or
working directory. Version 1 preferences load with an empty library and migrate on
the next save. Version 2 entries load without inventing saved versions and migrate
on the next save; loading alone leaves the original file untouched.
Version 3 retains its library/history and loads with provider setup required;
the next save migrates it to version 5 without altering existing snapshots.
Version 4 retains provider settings and metadata history, with no invented card lists;
it also migrates only on the next save.
Entries contain a stable ID, trimmed name, and creation timestamp;
duplicate names are allowed and distinguished by timestamp and ID. Navigation and
deck creation share one application session so saving preferences retains decks.
Each explicitly saved metadata version has a stable ID and timestamp. Unchanged
saves do not create duplicates. Opening a historical version replaces unsaved
edits in the editor; saving changes creates a new latest snapshot and retains all
earlier snapshots. Commander identity and sideboard notes are player-authored
text, retained across format changes, not resolved card data or legality results.
Missing state starts with default preferences. Empty, malformed, or unsupported
documents offer a reset or continuation without saving. Reset preserves the original
bytes in a uniquely named .recovery file alongside the state document. Unavailable
storage disables saving for that launch and reports how to retry. Saves flush a
temporary file in the same directory before replacing the previous document.
The store serializes operations within its instance; simultaneous application
processes are not coordinated.
PastedDecklistParser.Parse returns a platform-neutral CanonicalDecklist with
immutable card entries, exact raw text, a Pasted source, and an optional verbatim
source URL. DeckLibrarySession.SavePastedVersionAsync saves it alongside the
selected Commander/Legacy metadata through the existing version-history path.
Commander entries identify commander card names separately from player-authored
commander notes; they do not represent resolved color identity.
Supported lines are positive integer quantities followed by names (4 Brainstorm,
1x Sol Ring, or 2X Island). Bare lists default to mainboard. Case-insensitive
headers include Commander/Commanders, Deck/Main/Mainboard/Main Board, and
Sideboard/Side Board. Headers may have a trailing colon, square brackets, a //
prefix, or a parenthesized count. Card-type headings (such as Creatures or Lands)
select mainboard. SB: prefixes place individual lines in the sideboard without
changing the current section. Blank lines are ignored.
Card names are trimmed, whitespace runs collapse to one space, and casing becomes
invariant uppercase; punctuation is retained. Duplicate names sum within each
section. Output sorts by Commander, Mainboard, Sideboard, then ordinal card name.
Raw text is never normalized. Unsupported lines, invalid quantities, and empty
lists fail the whole parse with FormatException; detailed diagnostics, printing
annotations, card resolution, and legality are deferred.
Metadata-only saves retain the selected base version's card list and provenance when editing history, or the latest snapshot when no base version is selected. Identical metadata, cards, and provenance reuse the latest version. Changed raw text or source URL creates a new version even if the normalized cards are unchanged. Earlier versions remain fixed, including metadata-only snapshots from older files.
T06 validation: the final focused application/storage suite passed 79 tests, including Commander and Legacy parsing, sideboard subdivisions, raw-text/source round trips, version 4 migration, selected historical snapshot edits, malformed input rejection, and concurrent pasted saves. The direct serial desktop build passed with zero warnings/errors.
T07 validation: the final application/storage suite passed 122 tests, including distinct malformed, unsupported, unknown, ambiguous, unavailable-resolution, and resolver-failure diagnostics; partial-entry inspection; blocking saves; and provenance-preserving valid imports. The direct serial desktop build passed with zero warnings/errors. T09 adds a bounded offline demo card resolver for first-draft imports; complete card-facts/current-rules integration remains deferred to later tickets, and the paste UI remains deferred to the import-surface ticket.
T08 validation: the fixture and application suite passed 179 tests, including Moxfield URL validation, Commander/Legacy and sideboard mapping, raw payload and source URL persistence, fetch/parse/validation diagnostics, cancellation, notes preservation, and the desktop import action. The direct serial desktop build passed with zero warnings/errors. The live desktop action reached Moxfield and displayed its source-specific HTTP 403 diagnostic, but the current public endpoint also returned 403 to an anonymous request, so successful public retrieval remains open pending a supported anonymous Moxfield boundary or an explicit credentialed integration decision. No credentials or cookies were added.
T09 validation: the accepted public TappedOut URL import implementation passed all 258 application tests, covering Commander/Legacy fixture mapping, canonical raw payload and source URL provenance, blocking diagnostics, save-boundary behavior, and cancellation. The bounded offline demo card resolver supports first-draft imports while unknown or ambiguous card names still block saves. The direct serial Windows build passed with zero warnings/errors. The live desktop harness reached the TappedOut importer but received a source-specific retrieval failure, so no version was saved. This is an external access blocker; no cookies or credentials were added.
T10 validation: the current card-facts boundary uses Scryfall exact-name lookups with an explicit 24-hour in-memory freshness policy and no stale fallback. Requests share 120ms pacing and a 429 cooldown, with no retry. A Gatherer clarification seam is available without live scraping. Card-facts lookups cause no persistence, legality calculation, or import side effect. The validated serial suite passed 296 tests, and the direct Windows build passed with 0 warnings and 0 errors. The live Scryfall provider harness resolved Sol Ring.
Jev and generative AI are core product capabilities, not optional features. Jev is the structured decision layer; generative AI explains structured findings and coaches the player. All AI-assisted work is explicit and transparent. Open AI providers and privacy in the navigation rail to configure each provider's endpoint, model or deployment, and credential environment-variable name. There is no AI enable/disable feature gate. A fresh installation has no preselected endpoints. When provider-backed actions are implemented, configured destinations and credentials are operational prerequisites for those actions, not a product feature toggle. Explicit evaluation and playtest actions control when relevant data leaves the device for AI providers. Save applies changes locally; leaving the page discards unsaved edits. The page shows the saved recipients and explains which deck, goal, and evaluation data each provider may receive during explicit user actions.
Credentials are configured outside the app in environment variables; only their names are saved. This screen never resolves credential values. Settings are plain text, so never paste secrets into endpoints, model names, or variable-name fields. Endpoints require HTTPS except for loopback HTTP and cannot contain user info, query strings, or fragments. No provider requests, evaluation logic, telemetry, or background synchronization are implemented by this ticket. The disclosure also identifies public imports and current card/rules lookups as the other external boundaries. Provider settings share the existing application state interface and serialized session so library, history, and navigation saves preserve them.
Run the focused persistence and application tests without a MAUI workload:
dotnet test tests/MagicJev.Application.Tests/MagicJev.Application.Tests.csproj -m:1 -nr:false -p:UseSharedCompilation=falseT05 validation: all 64 focused application/storage tests passed, including provider
setup defaults, version 1–3 migration, settings round trips, concurrent saves,
failed-save retry, credential-reference isolation, and disclosure/recipient content.
Application and desktop projects built with zero warnings/errors using
dotnet build <project.csproj> --no-restore -m:1 -nr:false -p:UseSharedCompilation=false.
The live scripts/live-ai-settings.ps1 scenario passed: the disclosure was visible,
invalid endpoints were rejected, both provider settings saved locally, restart
restored saved values, unsaved edits were discarded, and the original settings
were restored. The standard solution build still returns failure without a
diagnostic; the direct serial desktop build passed.
T04 validation: 45 focused net10.0 tests passed, including metadata/history
round trips, opening older snapshots, saving an earlier state as a new version,
no-op saves, concurrent saves, failed-save retry, immutable collections, version 2
migration, and corrupt-history preservation. The full MAUI build and live UI
check now pass for the basic flow through scripts/live-library-scenario.ps1:
create an entry, save Commander metadata as version 1, return to the library,
reopen the entry, and confirm the selected saved version and version metadata.
Broader multi-snapshot editing remains covered by the focused tests.
T03 validation: 35 focused net10.0 tests passed, including create/list/select,
duplicate names, blank-name rejection, restart persistence, concurrent library and
navigation changes, version 1 migration, and failed-save retry. For a live manual
smoke test, the live library scenario creates and reopens a named entry successfully.
Duplicate-name coverage remains in the focused tests.
T02 validation: 24 focused net10.0 tests passed, including save/reload through a
new store instance, recovery with original-file preservation, unavailable storage,
failed replacement, and cancellation. The direct serial desktop build and the
live library, AI/privacy, and navigation scenarios pass; the standard solution
build remains blocked by the workload-resolver issue described below.
Earlier runs hit Defender Exploit Guard event 1121 for MagicJev.Desktop.exe and
the .NET workload resolver returned a solution-build failure without diagnostics.
After the approved local Defender exception and workload repair, the direct serial
desktop build and the live library and AI/privacy UI scenarios pass. The standard
solution build still needs separate workload-resolver cleanup before it can be
claimed as a passing gate.