The deployed toolbox Core.dll was timestamped BEFORE its own build ran -- the CI runner's
incremental build reused a cached SilverOS.Welcome.Core.dll, so source fixes (e.g. the winget
bootstrap brace fix) never reached the published exe. Wipe all bin/obj under welcome/ and pass
--no-incremental so every build is a clean compile.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
appinstall.log on the VM showed: bootstrap-winget exit=1 'Unexpected token }'. The
inline -Command was built from an interpolated string ($"...{{...}}" -> {/}) concatenated
with a NON-interpolated string whose '}}' stayed literal, so the emitted PowerShell ended
in '}}' and failed to parse -> the bootstrap (and thus winget install) never executed ->
all apps skipped on every run, regardless of network. Invoke the bootstrap .ps1 file
directly instead (it self-checks + installs winget online); fall back to the inbox
re-register only when the script is absent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Writes C:\ProgramData\SilverMetal\appinstall.log (best-effort) so a post-install mount
shows exactly where app installs fail: winget probe results, bootstrap-winget output,
and per-app winget exit codes. Makes the no-apps-installed failure diagnosable instead
of inferred.
The in-content Restart button overflowed its fixed width. Move it into the wizard
footer's right slot (where Next/Apply sits) as a btn-primary; Routes owns the restart
shutdown now, DoneStep just shows the recovery key.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The global .gitignore '*.exe' rule silently excluded netkvmp.exe + netkvmco.exe when the
NetKVM driver was committed, so only inf/sys/cat shipped. netkvm.inf REQUIRES netkvmp.exe
([SourceDisksFiles] + netkvmp.CopyFiles), so Add-WindowsDriver failed every build with 'the
driver package could not be installed' -> no virtio NIC driver -> no VM network. Un-ignore
windows/drivers/** and force-add the referenced binaries.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Build got through the ISO repack but failed copying the 5GB ISO to C:\silvermetal\out
('not enough space'): the build's working set (extracted ISO tree + expanded install.wim
+ 5GB base ISO) fills the single-volume runner, leaving <5GB for the persist copy. The
image grew again with the injected driver. Delete RUNNER_TEMP\smbuild + base.iso (no
longer needed post-build/validate) right before the copy to reclaim ~10GB.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add-WindowsDriver rejected the virtio NetKVM driver during offline servicing and
aborted the whole build. A driver issue must not brick the image: wrap it in try/catch
(warn + continue) and add -ForceUnsigned to bypass the offline-inject signature check
(the driver is WHQL-signed and loads at boot regardless). Add .gitattributes marking
driver/binary files as binary so the runner checkout never EOL-normalizes them.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The privacy hardening enables HVCI (Memory Integrity), which blocks the legacy
e1000 NIC driver (E1G6032E.sys) -> no network in the VM, so winget app installs
silently skip. virtio-net's NetKVM driver is WHQL-signed + HVCI-compatible. Staged
from virtio-win (w11/amd64) under windows/drivers/netkvm/; build.ps1 already auto-
injects any *.inf under windows/drivers/ into install.wim. Pair with a virtio NIC on
the VM (already switched). Lets apps actually install under hardening.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hands-on VM testing showed auto-apply skipped the app picker entirely -- the user
couldn't review/adjust apps before install. Land first-run on the Apps step instead
(pre-checked with the collector flavour's defaults); the user adjusts then walks
Apps -> Prefs -> Apply -> Done. The collector already owns account + flavour, so
Welcome/Flavour are skipped. Reverses the earlier auto-apply behavior per operator
feedback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The boot.wim Setup\CmdLine override (legacy-Setup forcing) is authoritative over
winpeshl.ini, so it launched setup.exe directly and the collector never ran -- the
VM went straight to the old sm-bootstrap unattended install. Repoint Setup\CmdLine
at the collector (cmd /c X:\sm\Start-Collector.cmd); the collector still launches the
legacy X:\sources\setup.exe itself. Add wpeinit + an on-screen banner, and write any
collector/WinForms-load failure to X:\sm\collector-error.txt shown on the console
before falling back, so we can diagnose WinForms-in-WinPE.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The boot.wim now carries WinPE-NetFx/PowerShell (collector), growing the image ~0.4GB,
and each build persists a ~5GB ISO to C:\silvermetal\out. On the single-volume runner
that accumulation starved oscdimg ('Insufficient disk space'). Clear prior output +
stale smbuild work dirs at job start so free space self-heals each run.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Welcome wizard showed nothing until WebView2 cold-started and Blazor
booted, so the whole startup cost presented as a blank window long enough
that operators thought first boot had failed.
- Native MAUI splash overlay (renders in the first frame, no WebView2/JIT
dependency) + a visually identical in-page splash inside #app, so the
native -> webview -> Blazor handoff reads as one continuous loading
screen. Fades out on first successful WV2 NavigationCompleted.
- PublishReadyToRun=true (publish-only) to remove first-run JIT on the
one-shot cold-disk path. R2R header verified present after publish.
- Fixed-version WebView2 runtime baked offline next to the exe (build.ps1
stages it, app points WEBVIEW2_BROWSER_EXECUTABLE_FOLDER at it). Removes
the Evergreen registry probe and the LTSC "no WebView2 at all" risk flagged
in welcome-app-spec.md; air-gap friendly. Absent => falls back to Evergreen.
- De-flash launch: drop the `cmd /c` wrapper and add -WindowStyle Hidden in
autounattend FirstLogonCommands (kills the console flash + one process).
Verified: Release build clean, win-x64 self-contained publish succeeds with
R2R confirmed, 38/38 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On IoT LTSC winget is absent, so Process.Start('winget') throws Win32Exception
('cannot find the file specified') rather than returning non-zero. That throw
propagated out of InstallAsync and failed the entire Apply ('Configuration failed').
AppInstaller is now fully exception-safe: a TryRunAsync wrapper converts launch
throws into a failed run, winget is resolved defensively (PATH -> bootstrap+re-probe
-> WindowsApps alias path) and when unavailable the installer skips apps and marks
them not-installed instead of throwing. Per-app launch throws are isolated too.
Two new tests cover probe-throws-skips and per-app-throw-isolated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Assert-IsoStructure.ps1 reused a fixed mount dir; a prior aborted run left a WIM
mounted there, so Mount-WindowsImage failed with 'directory is not empty' and the
persist-to-stable-path step was skipped (no ISO deployed). Now discards stale mounts
+ clears corrupt mount points + uses a unique per-run mount dir (mirrors build.ps1
Stage 0), and removes the dir after. Also asserts apps/catalog.json baked into the WIM.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>