Project instructions for AI agents working in this repository. Read this before building, cleaning, or releasing anything.
Every project has its own top-level folder beside the solution file — there are
no src/apps/tests/samples grouping folders. The solution mirrors the
disk (always keep the two in sync — a project's path in CSharp_XISOSharp.sln
must match its real folder):
| Folder | Contents |
|---|---|
XISOSharp/ |
Shipped library (NuGet: XISOSharp) |
XISOSharp.Cli/ |
Shipped command-line executable |
XISOSharp.Gui/ |
Shipped Avalonia desktop app |
XISOSharpTester/ |
Windows-only WPF regression rig |
XISOSharp.Sample/ |
Library walkthrough app |
XISOSharp.Tests/ |
xUnit suite |
XISOSharp.BattleTests/ |
CLI-vs-reference battle harness |
XISOSharp.Benchmarks/ |
BenchmarkDotNet harness |
XISOSharp.Fuzz/ |
Mutation fuzzer |
XISOSharp.AotSmoke/ |
NativeAOT publish/run smoke check |
XISOSharp.TestDataGenerator/ |
Deterministic TestData/ fixture generator |
docs/, References/, TestData/ |
Docs, vendored upstream sources, gitignored fixtures — stay at the repo root |
Directory.Build.propsdefines$(RepoRoot); cross-project paths (linkedCompileitems,ProjectReference, the strong-name key, staged CLI outputs) use it instead of..\..\chains. Add new cross-project references that way.TestDataLocatorfinds the repo root by walking up toCSharp_XISOSharp.sln, so keep the solution file at the root.TestData/stays there too.References/**is never part of the solution; it holds oracle binaries and third-party sources for parity work.
Release output trees are artifact stores, not disposable caches. They hold
previously shipped bundles (e.g.
XISOSharp.Gui/bin/Release/release_1.0.1_win-x64.zip) that may not be
reproducible or re-downloadable. Never delete them.
- Never delete or remove files under a project's Release output paths:
- any
*/bin/Release/**(library, CLI, GUI, Tests, Tester, Benchmarks, BattleTests, TestDataGenerator) publish/**,publish-gui/**(publish-script outputs)- any
release_*.zip,*.nupkg,*.snupkgfile anywhere
- any
- Never run destructive cleanups against them:
dotnet clean -c Release,Remove-Item -Recurseonbin/obj/publish*,git clean -xdf, etc. - Fresh builds go through a temp staging directory
(
%TEMP%\xiso_bundle\<rid>— safe to wipe) and only write/overwrite the exact artifact being rebuilt in the Release path. Creating new files and overwriting the same-named artifact for the current version is fine; deleting older or unrelated artifacts is not. - If a clean of a Release path seems required (corrupt output, TFM/RID change), stop and ask the user first.
.github/workflows/ci.ymlbuilds the full solution on ubuntu/windows/macos and runs the sequential test suite on all three. The suite restores the process working directory after each test; keep that invariant when adding CWD-sensitive tests.- The same workflow gates on
gui-smoke(CLI-v, GUI--probe-cli/--self-test),inspectcode(JetBrains InspectCode, pinned2026.2.3.1, fails on any warning/error and uploads SARIF),dependency-audit(dotnet list package --vulnerable),aot-smoke(NativeAOT publish + run),fuzz-smoke(2-minute mutation fuzz) andbattle-interop(synthetic copyright-free corpus vs the checked-in extract-xiso/xdvdfs/xboxkit oracles). Keep InspectCode at 0 warnings; the floor is enforced in CI. - Coverage: the Windows test job writes merged line/branch coverage to the job
summary and fails below
MIN_LINE_COVERAGE(70%) orMIN_BRANCH_COVERAGE(60%). - Formatting:
dotnet format whitespace --verify-no-changesruns on the Windows runner (the CRLF working tree matches.editorconfig); run it locally before pushing. - The test suite runs on Ubuntu, Windows and macOS (the suite restores the process working directory); Linux/macOS also build the Windows-only Tester/GUI projects.
- Companion workflows:
codeql.yml(CodeQL C#),dependency-review.yml(PRs),scorecard.yml(OpenSSF Scorecard),release.yml(tag: six RID bundles + GitHub release),pages.yml/wiki.yml(docs). - Tag pushes trigger the tag-only
publish-nugetjob; PR/branch runs stop at pack.release.ymlruns in parallel and creates the GitHub release. - The three TFM test hosts run in parallel and share one
TestData/tree; the fixture stampsTestData/output/fixture.stampso only the first host rebuilds the ISO (TestDataWriter). Do not make the ISO rebuild unconditional again. XISOSharp.Testsis strong-named withXISOSharp/XISOSharp.snkbecause the library grants itInternalsVisibleTowith the matching public key embedded inXISOSharp.csproj; keep the key file, the testSignAssemblysettings, and the embedded public key in sync when touching signing.
- 1.5.0 is published (2026-11-01). Do not reuse the version or move the
1.5.0tag again. The next release is at least1.6.0; the old 1.6.0 notes were merged into 1.5.0 and must not be published as a separate tag. - Version comes from git tags via MinVer. Never add or edit a
Versionproperty in a csproj. - Release tags are bare semver:
1.0.0,1.0.1,1.0.2(v1.0.2also works). N commits after tag1.0.1report1.0.2-alpha.0.N+<sha>. - NuGet versions are immutable: never reuse one.
--skip-duplicatemakes re-runs idempotent. - CI is
.github/workflows/ci.yml; its tag-onlypublish-nugetjob packs the library and pushesXISOSharp.<version>.nupkg+.snupkgusing theNUGET_API_KEYsecret in thenugetenvironment (already configured).
-
Finalize documentation first — the package bakes it in:
XISOSharp/README.mdis packed into the nupkg (PackageReadmeFile), andXISOSharp/XISOSharp.csprojholds the nuget.orgDescription.- Add the release section to
docs/release-notes.md, refreshWhatsNew.md, and update the READMEs (root, library, CLI, Tests, Tester) plus any docs the change touched. - Upstream parity references use the dated build (
extract-xisobuild202609111233), notv2.7.1. Never change the on-disk optimized tagin!xiso!2.7.1 (01.11.14)orConstants.ExisoVersion.
-
Run the full suite:
dotnet test XISOSharp.Tests/XISOSharp.Tests.csproj -c Release(currently 2098 passed / 1 skipped on net8.0/net9.0 and 2162 passed / 1 skipped on net10.0 — net10.0 adds the GUI-helper tests). -
Commit + push
master(only when the user asks), then tag and push:git tag <version> && git push origin <version>. -
The tag push triggers CI pack + publish. If the
nugetenvironment has protection rules, approve the deployment in GitHub Actions. Verify withgh run list --workflow=ci.ymlandhttps://api.nuget.org/v3-flatcontainer/xisosharp/index.json(indexing can lag a few minutes). -
Manual fallback (CI unavailable or local verification):
dotnet pack XISOSharp/XISOSharp.csproj -c Release -o artifacts\<version> dotnet nuget push "artifacts\<version>\XISOSharp.<version>.nupkg" --api-key $env:NUGET_API_KEY --source https://api.nuget.org/v3/index.json --skip-duplicate dotnet nuget push "artifacts\<version>\XISOSharp.<version>.snupkg" --api-key $env:NUGET_API_KEY --source https://api.nuget.org/v3/index.json --skip-duplicate
Push the nupkg before the snupkg (symbols require the package to exist).
GitHub releases carry only the six RID app bundles; the library packages stay
NuGet-only (user decision). The CLI's [UPDATE] check downloads
release_<version>_<rid>.zip from the latest release, so asset names and the
release tag must match the version exactly.
Automated path (default): .github/workflows/release.yml runs on the tag
push, builds all six bundles on ubuntu-latest with the recipe below, smoke-tests
win-x64/linux-x64/MacOsX-arm64, and creates the release from the tag with
the version's docs/release-notes.md section as the body. Re-runs refresh the
same-named assets (gh release upload --clobber) and never delete other assets.
To rehearse without tagging, dispatch the workflow with dry_run: true.
Manual fallback (CI unavailable or local verification):
-
Build the bundles (recipe below) for:
win-x64,win-arm64,linux-x64,linux-arm64,MacOsX-x64,MacOsX-arm64. -
Create the release from the pushed tag. The body is the version's section of
docs/release-notes.md:gh release create <version> --title <version> --notes-file <version-notes.md>
-
Upload all six bundles:
gh release upload <version> XISOSharp.Gui/bin/Release/release_<version>_win-x64.zip \ XISOSharp.Gui/bin/Release/release_<version>_win-arm64.zip \ XISOSharp.Gui/bin/Release/release_<version>_linux-x64.zip \ XISOSharp.Gui/bin/Release/release_<version>_linux-arm64.zip \ XISOSharp.Gui/bin/Release/release_<version>_MacOsX-x64.zip \ XISOSharp.Gui/bin/Release/release_<version>_MacOsX-arm64.zip
-
Verify:
gh release view <version> --json assetslists exactly the six zips.https://github.com/purelogiccode/XISOSharp/releases/download/<version>/release_<version>_win-x64.zipreturns HTTP 200.
-
Never attach
*.nupkg/*.snupkgto the release, and never delete existing release assets unless the user asks.
Outputs need the .NET 10 runtime (Desktop Runtime for the GUI). For each RID,
stage into %TEMP% (safe to wipe) and produce the zip under
XISOSharp.Gui/bin/Release/:
$stage = "$env:TEMP\xiso_bundle\<rid>"
# create/clear ONLY the temp stage
dotnet publish XISOSharp.Gui/XISOSharp.Gui.csproj -c Release -r <rid> --no-self-contained -o $stage
dotnet publish XISOSharp.Cli/XISOSharp.Cli.csproj -c Release -f net10.0 -r <rid> --no-self-contained -p:PublishTrimmed=false -o $stage
Get-ChildItem $stage -Include *.pdb,*.xml -Recurse | Remove-Item -Force
Copy-Item README.md $stage
Copy-Item LICENSE $stage
Copy-Item WhatsNew.md $stage
Compress-Archive "$stage\*" "XISOSharp.Gui\bin\Release\release_<version>_<rid>.zip" -ForceEach zip must contain exactly the GUI app, XISOSharp(.exe), README.md,
LICENSE, and WhatsNew.md — no *.pdb / *.xml.
Smoke-test from an extracted zip:
XISOSharp.exe -v→XISOSharp v<version> for <platform> - https://github.com/purelogiccode/XISOSharpXISOSharp.Gui.exe --probe-cli→XISOSharp <version>XISOSharp.Gui.exe --self-test <path-to-cli>→SELF-TEST: all passed