Commit Graph

187 Commits

Author SHA1 Message Date
sysadmin
18475fa731 feat(toolbox): first-run lands on the Apps picker (not silent auto-apply)
Some checks failed
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Failing after 6m14s
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>
2026-06-10 14:11:28 +01:00
04a6f6eabb Merge pull request 'fix(collector): carry preconfig via chunked FirstLogonCommands (specialize Path too long)' (#24) from fix/answer-file-specialize-length into main
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (push) Successful in 7m34s
2026-06-10 09:35:34 +00:00
sysadmin
7e99d7e304 fix(collector): carry preconfig via chunked FirstLogonCommands (specialize Path was too long -> answer file invalid)
Some checks failed
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Failing after 5m44s
2026-06-10 10:34:47 +01:00
731ae88adf Merge pull request 'fix(collector): launch via Setup\CmdLine (collector was bypassed into Setup)' (#23) from fix/collector-launch-via-setup-cmdline into main
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (push) Successful in 5m47s
2026-06-10 09:14:19 +00:00
sysadmin
fce4b77bd6 fix(collector): launch via Setup\CmdLine (was bypassed) + WinPE diagnostics
Some checks failed
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Failing after 4m8s
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>
2026-06-10 10:14:08 +01:00
3538f43267 Merge pull request 'ci(windows): free disk space before build (fixes oscdimg out-of-space)' (#22) from ci/free-disk-before-build into main
Some checks failed
Build SilverMetal Enhanced - Windows ISO / build (push) Failing after 24s
2026-06-10 08:50:14 +00:00
sysadmin
7eec584a66 ci(windows): free disk space before build (clear prior ISO output)
Some checks failed
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Failing after 21s
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>
2026-06-10 09:50:00 +01:00
c154e70495 Merge pull request 'feat: WinPE pre-config collector + simplified first-boot toolbox (SP1)' (#21) from docs/winpe-preconfig-collector into main
Some checks failed
Build SilverMetal Enhanced - Windows ISO / build (push) Failing after 3m51s
2026-06-10 08:40:57 +00:00
sysadmin
e6c292da25 ci(windows): install ADK WinPE add-on so boot.wim collector can be staged
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Successful in 7m26s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 09:38:13 +01:00
sysadmin
6c96e92fa5 fix(collector): drop 'essentials' from flavour radios (it's the baseline role, not a flavour)
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Successful in 5m18s
2026-06-10 09:35:59 +01:00
sysadmin
a82ca271a0 feat(build): scrub Panther unattend + assert collector baked into boot.wim
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 09:28:45 +01:00
bd215cba54 Merge pull request 'perf(welcome): cut first-boot cold-start + add loading affordance' (#20) from fix/welcome-cold-start into main
Some checks failed
Build SilverMetal Enhanced - Windows ISO / build (push) Failing after 21s
Reviewed-on: #20
2026-06-10 08:27:18 +00:00
sysadmin
084dd6a1d7 fix(collector): pre-launch XML parse-check (fail to default) + resolve setup.exe path 2026-06-10 09:25:57 +01:00
sysadmin
9d05a4a223 feat(build): stage WinPE collector into boot.wim (winpeshl + WinPE-NetFx/PowerShell) with SM_UNATTENDED fallback
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 09:22:14 +01:00
sysadmin
5bc345b1bd feat(toolbox): first-run auto-applies the collected preconfig (no manual walkthrough) 2026-06-10 09:19:11 +01:00
sysadmin
e88e476cd6 feat(toolbox): drop Account step, pre-seed from preconfig, run-once vs toolbox-home
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 09:12:39 +01:00
sysadmin
2730b29cb6 refactor(toolbox): drop unused IProcessRunner from ApplyService ctor
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 09:08:14 +01:00
sysadmin
30a168e853 perf(welcome): cut first-boot cold-start + add loading affordance
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Successful in 4m46s
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>
2026-06-10 09:06:02 +01:00
sysadmin
f3d66fb9d3 refactor(toolbox): Apply is apps+bitlocker only (account via Setup, hardening via SetupComplete)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 09:04:09 +01:00
sysadmin
bd1e2885df feat(toolbox): preconfig store (load fail-open, clear-pin, configured marker)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 08:56:32 +01:00
sysadmin
42d86734b0 feat(collector): answer-file generator (real account, no sm-bootstrap, embedded preconfig) 2026-06-10 08:51:35 +01:00
sysadmin
72e401113a feat(collector): WinPE input validation helpers + Pester tests 2026-06-10 08:45:58 +01:00
sysadmin
27a08ac1ab docs(welcome): WinPE pre-config collector implementation plan (SP1)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 08:37:15 +01:00
sysadmin
59418e37c8 docs(welcome): WinPE pre-config collector design spec (SP1)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 08:30:48 +01:00
72fa329ddd Merge pull request 'fix(apps): winget launch failure no longer crashes Apply' (#19) from fix/winget-launch-resilience into main
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (push) Successful in 6m30s
2026-06-10 00:24:02 +00:00
sysadmin
3daa770584 fix(apps): winget launch failure no longer crashes Apply
Some checks failed
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Failing after 4m44s
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>
2026-06-10 01:23:49 +01:00
f6dac0fdfd Merge pull request 'fix(ci): ISO-assert discards stale WIM mount (was blocking ISO persist)' (#18) from fix/iso-assert-stale-mount into main
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (push) Successful in 5m45s
2026-06-10 00:02:42 +00:00
sysadmin
4268a337f3 fix(ci): ISO-assert discards stale WIM mount + asserts app catalog baked
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Successful in 5m55s
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>
2026-06-10 01:02:31 +01:00
129b8741fd Merge pull request 'feat(welcome): per-role app recipes in the first-boot wizard' (#17) from feat/app-recipes into main
Some checks failed
Build SilverMetal Enhanced - Windows ISO / build (push) Failing after 5m27s
2026-06-09 23:54:29 +00:00
sysadmin
260023a1a5 feat(apps): catalog.json + chromium configure + winget bootstrap + build staging
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Successful in 6m36s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 00:41:18 +01:00
sysadmin
3c7654f128 feat(apps): install selected apps during Apply (after accounts, before BitLocker)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 00:36:54 +01:00
sysadmin
de18ee8dd2 feat(apps): insert Apps step after Flavour + seed per-role defaults
Insert AppsStep as wizard index 2 (renumbering Account/Prefs/Apply/Done
to 3-6), load the app catalog alongside flavours, seed the per-role
default selection on entering the step, and register IAppCatalog in DI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 00:27:18 +01:00
sysadmin
993d4028a2 feat(apps): AppsStep grouped checkboxes
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 00:27:11 +01:00
sysadmin
52d7187991 feat(apps): WizardState.SelectedApps
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 00:27:06 +01:00
sysadmin
cd3808de64 feat(apps): winget install engine (bootstrap + per-app + configure, continue-on-failure)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 00:22:19 +01:00
sysadmin
18eb42324a feat(apps): AppCatalog loader + role filtering 2026-06-10 00:18:58 +01:00
sysadmin
bfb53bd295 feat(apps): AppCatalogEntry record + test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 00:16:10 +01:00
sysadmin
1e59029e53 docs(welcome): app-recipes implementation plan 2026-06-10 00:14:30 +01:00
sysadmin
58d261cc6b style(hardening): ascii-ify em-dash in kernel-credential comment 2026-06-10 00:14:30 +01:00
sysadmin
583ed4400c docs(welcome): role app-recipes design spec
Per-role app-install picker for the Welcome wizard: catalog.json + AppsStep + winget
install engine (phased, swappable source for a future curated mirror). Stack stays
auto-installed; picker adds role apps + privacy-trimmed essentials. Approved in
brainstorming. Next: writing-plans.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 23:58:37 +01:00
efdf5888ac Merge pull request 'fix(kiosk): keyboard filter covers admins + taskbar auto-hide + instant sm-bootstrap disable' (#16) from fix/kiosk-lockdown-polish into main
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (push) Successful in 5m43s
2026-06-09 22:36:42 +00:00
sysadmin
e83ce6bcf0 fix(kiosk): keyboard filter covers admins + taskbar auto-hide + disable sm-bootstrap in-session
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Successful in 5m0s
Live e2e: in the sm-bootstrap session the taskbar showed and Win/Start worked.
- Keyboard Filter EXEMPTS administrators by default and sm-bootstrap is an admin, so
  Win/Start/Alt-Tab etc. were never blocked. Set WEKF_Settings
  DisableKeyboardFilterForAdministrators=false so the filter applies to it.
- Auto-hide the taskbar (default-user StuckRects3, inherited by sm-bootstrap) so it
  doesn't peek over the fullscreen wizard.
- TearDownAsync now Disable-LocalUser's sm-bootstrap in-session (immediate) so it's
  unusable at once; the deferred SYSTEM task still deletes it on next boot (SAM-confirmed
  the delete works now).

Verified: Configure-Kiosk parses under Windows PowerShell 5.1 (ASCII-clean); welcome 29/29.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 23:30:43 +01:00
sysadmin
6124448003 fix(first-boot): branding-online parse crash (em-dash/encoding) + bootstrap cleanup task + recovery QR
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Successful in 4m47s
Found by reading the unencrypted VM disk after run #7:
1. Online branding never ran: Apply-Branding.ps1 had a UTF-8 em-dash in a Write-Warning
   STRING; Windows PowerShell 5.1 (SetupComplete) reads .ps1 as ANSI, mangled it, broke
   the string terminator -> whole script failed to parse -> lock/login/wallpaper branding
   never re-applied. Fix: ASCII-ify the em-dash AND save the branding scripts UTF-8-with-BOM
   so PS5.1 always decodes them correctly (verified parses under PS5.1 + PS7).
2. sm-bootstrap never removed: TearDownAsync used schtasks /tr with an inline -EncodedCommand,
   which silently fails past the ~261-char /tr limit, so the cleanup task was never created
   (confirmed NO_TASK on disk). Fix: Register-ScheduledTask (no length limit).
3. Done step: show a QR code of the BitLocker recovery key (QRCoder) for phone backup, and
   lay key+QR side-by-side so the Restart button no longer overflows below the fold.

Verified: welcome solution builds, 29/29 tests; branding Pester 6/6 unit (offline-integration
needs elevation, runs in CI).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 22:41:30 +01:00
5f0df87405 Merge pull request 'fix(welcome): BitLocker PIN first-boot + recovery-key display + FlavourStep Next' (#14) from feat/wizard-recipes into main
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (push) Successful in 6m3s
Reviewed-on: #14
2026-06-09 21:05:40 +00:00
sysadmin
a3623b1fbb fix(welcome): BitLocker PIN works first boot (drop -SkipHardwareTest) + show recovery key
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Successful in 7m5s
- BitLocker: remove -SkipHardwareTest so BitLocker validates the TPM+PIN unseal via
  its hardware test on the next reboot (the wizard's end-of-flow reboot) before
  encrypting — fixes the E_FVE_SECURE_BOOT_CHANGED / PCR-11 drop-to-recovery on the
  first post-enroll boot. The PIN now works first time instead of needing recovery.
- Done step now DISPLAYS the 48-digit BitLocker recovery key (read from the file the
  enrollment saves) with a 'save this' warning — previously it was never surfaced.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 21:57:47 +01:00
94de283495 Merge pull request 'fix(first-boot): online branding re-apply + deferred sm-bootstrap cleanup' (#13) from fix/branding-online-and-bootstrap-cleanup into main
Some checks failed
Build SilverMetal Enhanced - Windows ISO / build (push) Failing after 6m19s
Reviewed-on: #13
2026-06-09 20:57:29 +00:00
sysadmin
6d6eb2cdc8 fix(welcome): FlavourStep notifies host on select so Next enables immediately
WIP on local branch feat/wizard-recipes (NOT pushed) — holding per operator while
more wizard changes (role app-recipes) are designed.
2026-06-09 21:45:20 +01:00
sysadmin
daac231148 fix(first-boot): re-apply personalization branding online + defer sm-bootstrap cleanup
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Successful in 5m37s
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>
2026-06-09 21:27:24 +01:00
66ab2e1aed Merge pull request 'fix(bitlocker): add recovery-password protector + save key (TPM+PIN-only was unrecoverable)' (#12) from fix/bitlocker-recovery-key into main
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (push) Successful in 5m22s
Reviewed-on: #12
2026-06-09 20:24:36 +00:00
sysadmin
3f1ea6aa63 fix(bitlocker): add recovery-password protector + save the key (was unrecoverable)
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Successful in 6m17s
VM e2e: full wizard ran end-to-end and enrolled TPM+PIN, but BitLockerService only
created TPM+PIN with NO recovery protector — a forgotten/mistyped PIN bricks the
drive (hit exactly that on the VM). Add a RecoveryPassword protector and save the
48-digit key to ProgramData AND the unencrypted EFI System Partition (readable even
when the OS volume is locked, e.g. for offline recovery/verification).

PRODUCT TODO (follow-up): escrow the recovery key to SilverSync + display it in the
wizard's Done step so the end-user records it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 20:15:49 +01:00