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>
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 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 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>
VM e2e findings on the real-user desktop:
1. Lock/login screen + wallpaper NOT branded (OEM About WAS) — Windows resets the
offline-baked personalization (PersonalizationCSP / default-user wallpaper / FVE)
during OOBE, same class as the UAC reset. Fix: stage windows/branding/ into the
image and re-run Apply-Branding -Mode Online from SetupComplete (post-OOBE, as
SYSTEM) where it sticks. OEM About re-asserted harmlessly.
2. sm-bootstrap account still present after onboarding — TearDownAsync's in-session
Remove-LocalUser no-ops (can't delete the account you're logged in as). Fix: keep
the best-effort in-session attempt, but DEFER the real removal to a SYSTEM
AtStartup scheduled task that runs on next boot (sm-bootstrap not logged on),
removes the account + Win32_UserProfile, then deletes itself.
(Network 'no adapter' in the VM was a Proxmox NIC-model regression to virtio — fixed
by switching the VM to Intel e1000; not a SilverMetal change.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A prior aborted build left a DISM image mounted in the fixed WorkDir,
locking install.wim and breaking the Stage 2 extract clean-up. Add a
Stage 0 that discards any orphaned SilverMetal mounts + loaded hives
before recreating the work dirs, and run CI in an ephemeral per-job
RUNNER_TEMP WorkDir so concurrent/aborted runs can't collide.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds Invoke-PublishWelcome (dotnet publish win-x64 self-contained, runs pre-mount)
and Copy-WelcomePayload (copies publish output + flavours/*.json into $mount while
install.wim is open) called from Invoke-ServiceWim's try block. Both are gated on
SILVERMETAL_WELCOME_ENABLED != '0' (enabled by default). Hardening staging unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The no-prompt efisys + media-first boot order reboot-loops: every post-copy reboot
re-boots the media before the disk install completes, so it never finishes (symptom:
"no bootable device" after ejecting). Standard efisys.bin (press-any-key) lets reboots
fall through to the installed disk. Legacy-Setup boot.wim patch + /unattend retained
(the real fix). Documented VM-verified result + the residual one-click WinPE language
page in iso-builder.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Legacy Setup (forced via boot.wim CmdLine) still showed the language page because
implicit answer-file search is unreliable when setup is launched via CmdLine. Inject
autounattend.xml into boot.wim (X:\autounattend.xml) and set CmdLine to
"X:\sources\setup.exe /unattend:X:\autounattend.xml" so all passes are consumed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
VM test proved Win11 24H2 redesigned "ConX" Setup ignores the windowsPE pass of
autounattend.xml (manual language/keyboard/region prompts). Deep-research-verified
fix: patch sources\boot.wim index 2 to launch the legacy installer.
build.ps1 stage 2b: mount boot.wim idx2, load offline SYSTEM hive, set
HKLM\SYSTEM\Setup\CmdLine=X:\sources\setup.exe, unload, commit. Also place
autounattend.xml in \sources as well as ISO root. Legacy engine consumes all
four passes -> fully hands-off. Documented in iso-builder.md §3a (incl. rejected
winpeshl.ini / RunSynchronous alternatives + ConX-may-change caveat).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
VM boot test proved the ISO boots under UEFI+SecureBoot+TPM2 but stopped at the
"press any key" prompt and (post-boot) the disk screen. Enable hands-off install:
- build.ps1: use efisys_noprompt.bin (fall back to efisys.bin) so the ISO boots
without a keypress.
- autounattend.xml: add GPT/UEFI DiskConfiguration (wipe disk 0 -> EFI/MSR/Win),
ImageInstall index 1, AcceptEula (eval = no key). Bootstrap local-admin pw is a
PLACEHOLDER the SKU pipeline must replace.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stages 1-5 pass; oscdimg failed with Error 123 because PowerShell doubled the
embedded quotes in -bootdata. Work paths have no spaces, so omit the inner
quotes around etfsboot.com/efisys.bin entirely.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implement build.ps1 (M2): mount/extract the base ISO, offline-service
install.wim (inject GPD drivers if staged, debloat appx, bake SetupComplete.cmd
+ hardening modules into \Windows\Setup\Scripts), inject autounattend.xml,
oscdimg UEFI repack, emit SHA-256 + SBOM. Elevation + oscdimg guarded.
Add .gitea/workflows/build-iso-windows.yaml: runs on the self-hosted
silverlabs-runner-win (windows-latest), ensures ADK Deployment Tools, acquires
the base ISO from repo var SILVERMETAL_BASE_ISO_URL or a pre-staged path, builds,
validates the baked payload offline, uploads SBOM/SHA (+ISO on dispatch/tag),
attaches to a Gitea release on win-v* tags. Mirrors build-iso-linux.yaml.
Add tests/Assert-IsoStructure.ps1: the no-nested-virt CI gate - mounts the built
ISO + install.wim read-only and asserts autounattend.xml, SetupComplete.cmd, and
the hardening modules are correctly baked. Full QEMU boot+Verify is a follow-on.
Switch autounattend to Windows' native SetupComplete.cmd auto-run (SYSTEM, end
of setup) instead of a duplicate FirstLogonCommands call.
Untested until first runner execution (dev box is ARM64). All PS parse-clean;
autounattend XML + workflow YAML valid.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>