Skip to content

Repository files navigation

Magic Deck Workbench

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.

Local development

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-windows

The 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-build

The 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.

Windows smoke test

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.ps1

For 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.

Boundaries

  • src/MagicJev.Desktop: MAUI presentation and composition root (MauiProgram). Platforms/Windows exclusively owns native Windows startup and manifests.

  • src/MagicJev.Application: plain net10.0 application boundary and stable shell routes. References Domain; has no MAUI or Windows dependencies.

  • src/MagicJev.Domain: plain net10.0 domain 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: plain net10.0 JSON 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.

Local application state

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.

Pasted decklist versions

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.

AI provider configuration and privacy

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=false

T05 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.

Current launch validation note

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.

About

AI-driven Magic: The Gathering deck evaluation, improvement, and playtesting workbench

Resources

Code of conduct

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages