Skip to content

Maintenance release v1.1.0: .NET 10, defect fixes, CI and repo hardening - #11

Merged
tsudo merged 7 commits into
mainfrom
chore/net10-maintenance-release
Aug 15, 2026
Merged

Maintenance release v1.1.0: .NET 10, defect fixes, CI and repo hardening#11
tsudo merged 7 commits into
mainfrom
chore/net10-maintenance-release

Conversation

@tsudo

@tsudo tsudo commented Aug 15, 2026

Copy link
Copy Markdown
Owner

What this changes

Maintenance release. No new operations — this moves the runtime off a version heading for end of support, fixes two defects, and closes the Material gaps from the pre-publish repo audit.

Why

.NET 9 reaches end of support on 2026-11-10. FieldKit publishes as SelfContained + PublishSingleFile, so the runtime is embedded in the shipped exe — every download after that date would have carried an unpatched runtime with no servicing path. .NET 10 is LTS through 2028-11-14.

Contents

Commit Change
b9092e5 Runtime to .NET 10 LTS, packages to 10.0.11, SDK pinned in global.json
c1de7f9 Remove duplicate Logger; handle unhandled UI exceptions
faf0f4c CI verifies the published artifact; action and runtime pins bumped
b3a91b6 CodeQL, CHANGELOG.md, issue and PR templates
0cb5977 CodeQL actions: read permission

The Logger defect

MainWindow's parameterless constructor created a Logger, and the real constructor chained through it via : this() before overwriting the field. The orphan opened a StreamWriter that nothing ever disposed.

The symptom was on disk: five zero-byte FieldKit-*.log files in the build output directory, one per launch. App already held the %TEMP% log path with FileShare.Read, so the orphan's open failed, the Logger constructor's fallback swallowed it, and it landed in AppContext.BaseDirectory instead.

App.xaml uses Startup= with no StartupUri, so MainWindow is only ever built through the two-arg overload — the parameterless constructor is deleted rather than patched.

Why CI changed

dotnet build never exercised single-file packing, self-contained runtime bundling, or compression, so CI was green on a configuration that is not the one released. There is now a publish step and a size assertion that fails if the self-contained runtime goes missing.

How it was verified

Notes for the reviewer

Pre-publish audit evidence: Notes/ATLAS/analysis/github-repo-audit_tsudo-fieldkit_2026-08-15.md (paved-road run PR-20260814-001).

Not in scope, deliberately: the dead dryRun plumbing (32 references across 12 task classes, left behind when Preview Only was removed in a6d6ff3) — kept separate so the runtime upgrade stays bisectable.

tsudo and others added 5 commits August 15, 2026 14:02
.NET 9 enters EOL on 2026-11-10. Because FieldKit publishes
SelfContained + PublishSingleFile, the runtime is baked into the exe,
so every download after that date would ship an unpatched runtime with
no servicing path. .NET 10 is LTS through 2028-11-14.

- TargetFramework net9.0-windows -> net10.0-windows
- System.Management and System.ServiceProcess.ServiceController
  9.0.0 -> 10.0.11 (supersedes stale Dependabot PRs #9 and #10,
  which proposed 10.0.9)
- Version 1.0.0 -> 1.1.0, app.manifest assemblyIdentity to match
- Pin the SDK in global.json so the build is reproducible rather than
  floating to whatever SDK is newest on the machine
- Update README build instructions and ROADMAP UI note

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
MainWindow's parameterless constructor created a Logger, and the real
constructor chained through it via `: this()` before overwriting the
field. The orphaned Logger opened a StreamWriter that nothing ever
disposed.

The symptom was on disk: five zero-byte FieldKit-*.log files in the
build output directory, one per app launch. App already held the %TEMP%
log path with FileShare.Read, so the orphan's open failed, the Logger
constructor's fallback swallowed it, and it landed in
AppContext.BaseDirectory instead.

App.xaml uses Startup= with no StartupUri, so MainWindow is only ever
constructed through the two-arg overload. The parameterless constructor
is deleted rather than patched.

Also adds a DispatcherUnhandledException handler. Tasks already catch
their own failures, so anything reaching it is a UI-layer bug; the app
now logs it and keeps the log path visible instead of vanishing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`dotnet build` never exercised single-file packing, self-contained
runtime bundling, or compression, so CI was green on a configuration
that is not the one released. Adds a publish step plus a size assertion
that fails if the self-contained runtime goes missing.

- dotnet-version 9.0.x -> 10.0.x
- actions/checkout v5 -> v7, actions/setup-dotnet v4 -> v6
- gitignore publish/ so the new output directory stays untracked

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Closes three Material gaps from the pre-publish repo audit run for the
v1.1.0 release.

- CodeQL static analysis on push, PR, and a weekly schedule. Uses manual
  build mode rather than buildless so the analyzer gets full type
  resolution across the partial classes WPF generates from XAML.
  Pinned to codeql-action v4 (the repo's "latest" release is a bundle
  tag, not the action major).
- CHANGELOG.md covering v1.0.0 and v1.1.0, in Keep a Changelog format.
- Issue forms for bugs and features, a PR template, and a security
  contact link that routes reports to the private advisory flow instead
  of a public issue.
- README: .NET badge 9.0 -> 10.0, and a changelog link from Download.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The analyze step reads workflow run metadata; without actions:read it
fails on some repo configurations.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

tsudo and others added 2 commits August 15, 2026 14:46
Review of the previous commit found that the new DispatcherUnhandledException
handler turned a crash into a false success report.

RunSelectedTasksAsync's finally block unconditionally set the status bar to
"Run complete" and the progress bar to 100%, and ShowCompletionSummary sat
outside that finally. So an exception escaping the loop meant: remaining
operations never ran, the in-flight row stayed "Running", no summary appeared
-- and the UI asserted the run had finished. Before the handler existed the
process died, which is wrong but unmistakable.

The run loop now owns its own failure: logs the abort, marks unfinished
operations "Aborted", shows a distinct dialog, and reports "Run aborted" at 0%.
Verified with a structural harness covering clean run, escaped exception, and
contained task failure -- 7/7 assertions.

Two related fixes:
- The handler was subscribed before the startup sequence, contradicting its own
  doc comment and killing the deliberate Shutdown(1) path for failures inside
  the disclaimer dialog. It now subscribes after the main window shows.
- Closing the window mid-run called _runCts.Dispose() while the run loop still
  read the token, so every remaining operation failed with
  ObjectDisposedException -- reading in the log as many broken operations
  rather than one lifecycle bug. Now cancels and lets the loop dispose.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The publish check asserted only that FieldKit.exe existed and was >= 20 MB.
Measured: publishing with IncludeNativeLibrariesForSelfExtract=false yields a
58.8 MB exe that passes that floor while scattering five native DLLs beside it
(D3DCompiler_47_cor3, PenImc_cor3, PresentationNative_cor3, vcruntime140_cor3,
wpfgfx_cor3). Uploading just the exe would then ship a requireAdministrator
binary that resolves those from its launch directory. The check now asserts the
publish directory contains nothing but the exe and its pdb, with a size band
rather than a one-sided floor.

- Retain the published exe as a build artifact. It was previously destroyed
  with the runner, so a failed assertion left nothing to inspect and the exe was
  never smoke-tested off the author's machine.
- global.json claimed to pin the SDK; rollForward latestFeature is a floor, not
  a pin, so both the README and CHANGELOG asserted a guarantee the config
  disabled. Kept as a floor -- the SDK in use determines which runtime is
  embedded in the self-contained exe, and a self-contained app only gets runtime
  fixes when rebuilt -- and lowered it to 10.0.100 so contributors on the GA SDK
  are not blocked. Docs now describe it as a minimum.
- CodeQL also analyzes `actions`. GitHub already identifies this repo as having
  analyzable workflows, so the three workflow files added in this branch were
  the one thing it added scanning for that went unscanned.
- Dependabot now covers the github-actions ecosystem; without it the action
  pins get no update PRs and drift silently.
- Explicit permissions block on build.yml.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tsudo
tsudo merged commit 9ec9da4 into main Aug 15, 2026
5 checks passed
@tsudo
tsudo deleted the chore/net10-maintenance-release branch August 15, 2026 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants