Commit Graph

347 Commits

Author SHA1 Message Date
cproudlock
8fbae24b4d webapp: fix a11y contrast, heading hierarchy, prefix-safe modal actions
- Accessibility: dark ink (#00003d) on warning/success buttons, badges, and success alert - white text on #ff9500/#0ad64f failed WCAG AA (~2:1); dark-on-bright now passes.
- Heading hierarchy: sidebar brand h1 -> div.brand-title (with matching CSS selector), and standardized all page-header titles to a single <h1> per page (8 templates were h2).
- Prefix-safe modal JS: dashboard/backups/reports/enrollment delete+clone actions now build their form action from url_for(...'__K__'/'__F__').replace(...) instead of hardcoded paths, so they survive a URL-prefix mount.
2026-07-23 10:14:29 -04:00
cproudlock
18db077475 webapp: reskin to shopdb-flask design system
Adopt the shopdb-flask visual language across the PXE webapp (presentation only, Flask/Jinja logic unchanged):
- New static/pxe-theme.css: GE Aerospace palette (atmosphere-blue sidebar, sky-blue primary, avionics-green), Inter font stack, light/dark theming via data-theme + localStorage (key pxe-theme) with system-pref fallback, and card/button/table/form/badge/alert component styles layered over Bootstrap.
- base.html: shopdb-style sidebar (logo + title, nav sections, footer light/dark toggle) + theme boot script.
- All 13 content templates restyled to the new page-header + card/table/badge vocabulary; unattend_editor grouped per unattend-UX research.
- Fixed a pre-existing CRITICAL bug found during review: nested <form>s in image_config.html made Adopt submit the delete form and Delete-selected post every orphan filename regardless of checkboxes; split into standalone forms wired via the form= attribute.
Built by a Fable-orchestrated Opus workflow (17 agents). All 14 templates parse clean under Jinja2.
2026-07-23 10:05:09 -04:00
cproudlock
8df30017aa Playbook: reconcile live dnsmasq/samba/blancco hand-edits back into repo
The live PXE server carried working config the playbook would have clobbered on re-run. Committed back:
- dnsmasq: BIOS boot chain now the vendorclass PXEClient:Arch:00000 -> ipxe.pxe scheme (plus iPXE userclass HTTP chain), replacing the stale undionly.kpxe client-arch=0 scheme (undionly.kpxe was never staged in tftp-root). Bind by listen-address=172.16.9.1 + bind-dynamic instead of interface=+bind-interfaces.
- samba: add 'allow insecure wide links = yes' to the managed symlinks block (modern Samba silently disables wide links without it) and the [winpeapps_bios] read-only share.
- blancco iPXE menu: full BIOS/EFI arch split (direct-kernel vmlinuz-bde-linux / vmlinuz-ubuntu with ucode+kexec initrds) with a GRUB chain fallback, replacing the old single-line 'chain grubx64.efi'.
startnet.cmd was NOT a commit-back: repo is already ahead of the live boot.wim (deploy-pending).
2026-07-23 09:31:20 -04:00
cproudlock
b240cfea12 collections: add eDNC detection diagnostic scripts 2026-07-23 09:21:49 -04:00
cproudlock
e7be0f5391 gea-engineer: pre-stage .NET 3.5 + VC++ redists via FlatUnattend
Add RunSynchronous Orders 39-49: enable NetFx3 from staged sxs + install the VC++ 2005-2022 redistributables at specialize, before the first-logon engineering installer. Each command is if-exist guarded against the extra_engineering\prereqs payload, so gea-standard (which shares this template) skips them. Payloads staged on the share under gea-engineer Deploy\Applications\extra_engineering\prereqs.
2026-07-23 09:21:49 -04:00
cproudlock
0c4c2a245d Remove legacy ge-* image types (gea-only fleet)
Drop ge-standard/ge-engineer/ge-shopfloor-lockdown/ge-shopfloor-mce from image_types + standard_types, both startnet boot menus (choices + labels + net use), and the webapp IMAGE_TYPES/FRIENDLY_NAMES. Fleet is gea-* only; the empty ge-* winpeapps stub dirs were removed on the live share.
2026-07-23 09:21:49 -04:00
cproudlock
fdf63ab32f Shopfloor self-heal: bootstrap recovery scripts + Keyence/WaxTrace heal
Fixes Keyence (and CMM/WaxTrace) imaging where the target got only partial
data: the Y: SMB mount goes idle-dead during WIM apply, so WinPE staging dies
early (often just after site-config.json + ppkg). The first-logon self-heal
was meant to recover but its scripts were themselves staged past the death
point, so nothing ran.

- FlatUnattendW10-shopfloor.xml: new FirstLogonCommands Order-4 bootstrap that
  mounts the enrollment share fresh and pulls Fetch-StagingPayload /
  Verify-And-Heal-Staging / enrollment scripts into C:\Enrollment before the
  Order 5/6 heal runs. Recovery no longer depends on WinPE staging surviving.
  Keep CommandLine <=320 and Description <=252 chars: exceeding the unattend
  schema length limits makes the whole oobeSystem pass invalid (OOBE prompts,
  no autologon).

- Verify-And-Heal-Staging.ps1: add Keyence and WaxTrace heavy-payload heal
  branches (previously only CMM). Keyence re-pulls installers-post\keyence\
  <model> -> C:\KeyenceInstall\<model>; WaxTrace re-pulls the bundle (minus
  formtracepak) plus the bay-matched FORMTRACEPAK-V<ver>.iso.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:12:43 -04:00
cproudlock
64e1f7e088 Report-AssetToShopDB: log OS + boot time in the POST line
Adds os= and boot= to the POST log line so report-asset-*.log shows whether
the PC actually grabbed the OS version and LastBootUpTime (uptime source).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 16:31:12 -04:00
cproudlock
287ec86c12 Report-AssetToShopDB: fleet-wide reporting, more fields, dual NIC
- collect logged-in user (console user via Win32_ComputerSystem, bare
  username), pc-type (C:\Enrollment\pc-type.txt), make/model, OS version
  (caption + DisplayVersion + build), last boot time (for uptime)
- report BOTH corp and controller NICs (physical only), each with MAC,
  tagged IsMachineNetwork; was corp-only before
- machine-number sourcing adds C:\Enrollment\cmm\cmmid.txt (CMM bay id)
  and skips the 9999 placeholder everywhere
- intended to run from common\ (every pc-type), not collections-only;
  api.asp patch-style update keeps it from clobbering other types

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 15:41:36 -04:00
cproudlock
85e7d91b1a CMM: imaging manifest installs goCMM 2.12.3 (was 1.1)
Future PXE images now install goCMM 2.12.3 (NSIS, /S silent, -> C:\Program Files
\goCMM, runs no-admin). Detection switched to File on goCMM.exe (new install
path). The tsgwp00525 SFLD enforce manifest is deliberately left on goCMM 1.1 so
already-deployed bays are NOT auto-upgraded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 10:45:25 -04:00
cproudlock
2461804fc8 CMM: Backup-CMM also captures the whole C:\Program Files\WAI tree
Adds a wai_backup_<PC>_<ts>.zip (robocopy /E of C:\Program Files\WAI + the x86
path) alongside the goCMM + PC-DMIS backups, indexed in cmm-backup-index.json.
Captures machine/controller content beyond the per-version PC-DMIS grab. Can be
multi-GB if WAI holds the full PC-DMIS 2016 install.

NOTE: this only CAPTURES it. sync-cmm-backups.sh + Restore-CMM still handle only
gocmm/pcdmis zips - staging/restoring the WAI zip needs those updated too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 10:40:01 -04:00
cproudlock
48bc609eb5 CMM/DODA: fix DODA-bay profile resolution + goCMM 2.12 DataFolder + settings converter
- Get-PCProfile: subtype-strip fallback. DODA bays set pc-subtype.txt=doda, so
  the profile key became "gea-shopfloor-cmm-doda" which matched NO profile/alias
  -> Get-PCProfile returned null -> callers fell to hardcoded defaults (no
  PC-DMIS desktop icons; Defect Tracker / WJ Shopfloor / Plant Apps force-started).
  Now an unmatched compound key falls back to the bare pc-type (-> CMM). VM-tested:
  gea-shopfloor-cmm/doda + CMM/doda resolve to CMM (7 apps, PC-DMIS present);
  non-CMM unaffected.
- 09-Setup-CMM Step 2.5c: Active Setup seed for goCMM 2.12 DataFolder. goCMM 2.12
  stores its shared-data-dir in HKCU\Software\General Electric\goCMM\DataFolder
  (decompiled: RegistrySettings uses Registry.CurrentUser - per-user). Imaging as
  SupportUser wouldn't reach the ShopFloor operator's HKCU. Active Setup runs the
  StubPath once per user at first logon -> every user gets DataFolder=C:\geaofi\.
  VM-tested: StubPath writes the value with the trailing backslash intact.
- Convert-goCMMSettings.ps1: converts legacy goCMM 1.1 ApplicationSettings.xml ->
  goCMM 2.12 goCMMSettings.xml schema. VM-tested: output byte-identical to a real
  goCMM-2.12-produced CMM10 goCMMSettings.xml.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 08:59:08 -04:00
cproudlock
132c57ab39 CMM: create C:\GE PC-DMIS FRONT END at imaging + move regserver helper to Public desktop
- Ensure-PCDMISFrontEnd.ps1/.bat: standalone fix for already-imaged bays -
  create C:\GE PC-DMIS FRONT END + grant Users/Auth Users Modify. PCDToIGES.exe
  writes its error log there in its catch block; on a fresh bay the dir is absent
  (legacy front-end setup isn't part of imaging), so ANY PCDToIGES error becomes
  an unhandled DirectoryNotFoundException that crashes the export and masks the
  real cause (confirmed live on a CMM bay).
- 09-Setup-CMM Step 2.5b: create that dir + ACL at imaging for every CMM bay.
- 09-Setup-CMM Step 2.5: Register-PCDMIS-COM.bat now lands on the Public desktop
  (visible to operator or SupportUser) instead of SupportUser-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 11:56:00 -04:00
cproudlock
99deaa6882 CMM: add PC-DMIS 2026.1 desktop shortcut + taskbar pin (site-config)
The CMM pcProfile listed PC-DMIS 2016 and 2019 R2 in desktopApps/taskbarPins but
not 2026.1, so freshly imaged 2026 bays (CMM11/12) got no PC-DMIS shortcut. Add
the 2026.1 entries pointing at C:\Program Files\Hexagon\PC-DMIS 2026.1 64-bit\
PCDLRN.exe. Test-Path gated by the shortcut generator, so it is a no-op on
2016/2019 bays.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 20:49:55 -04:00
cproudlock
948d013539 preinstall: add VC++ 2015-2022 x64 redist (PC-DMIS 2026 vcruntime140_1.dll)
The x64 redist ladder in preinstall stopped at 2013, so 64-bit PC-DMIS 2026 had
no vcruntime140_1.dll (introduced in VC++ 2019/14.20) and PCDLRN.exe failed to
launch: "vcruntime140_1.dll was not found". The patched PC-DMIS MSI bypasses
Hexagon's Burn bundle, which would otherwise have pulled the runtime in - and the
existing 2010/2012 x64 entries (added for PC-DMIS 2016/2019) never got a
2015-2022 successor.

Add VC++ 2015-2022 x64 (vc_redist.x64.exe 14.44.35211, /install /quiet
/norestart), PCTypes ["*"], File-detected on vcruntime140_1.dll so it is
version-independent. Installer staged on the share at
pre-install/installers/vcredist/2022-x64/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 20:43:35 -04:00
cproudlock
6e218f3daf CMM/DODA: Install-DODA does the full unattended runtime+app install
The vendor GE_DODA_build5_Installer.exe is a multi-app NSIS bundler whose /S
silent mode is broken (nested sub-install -> MSI 1619); VM-confirmed it only
works interactively. So Install-DODA now drives the bundled components directly,
all silent:
  python-2.7.16 msi /qn  -> C:\Python27
  openjdk-1.8.0.232 msi /qn -> C:\Program Files\RedHat
  gs927w64.exe /S        -> Ghostscript 9.27
  vc_redist /quiet
  doda_build*.zip        -> C:\Apps\DODA  (+ writes .settings the zip lacks)
Then sets AFDA_INSTALL_DIR + Java PATH, precompiles .py->.pyc as admin, and
grants Users+Auth Users Full on C:\Apps\DODA so the locked-down operator can run
DovetailAnalysis.exe (writes .pyc/PreProcess/$TMP). Each step detects-and-skips;
idempotent. VM-validated end to end incl a non-admin operator-paths test.

Runtime binaries (python/jdk/gs/vc msis + doda zip) are staged on the share, not
committed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 19:54:15 -04:00
cproudlock
b5b644a360 CMM/DODA: restore on DODA bays + grant Full on whole C:\Apps\DODA
- Restore-CMM: drop the skip-on-doda gate. DODA bays now restore the
  config-version PC-DMIS + goCMM settings like any other bay (they have backups
  now; DODA itself installs separately to C:\Apps\DODA and is unaffected).
- sync-cmm-backups.sh: update the stale "do not back up DODA bays" note.
- Install-DODA.ps1: grant Users + Authenticated Users Full on the WHOLE
  C:\Apps\DODA (was PreProcess only) - DODA writes output/temp throughout the
  folder as the locked-down operator. /T covers PreProcess.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 17:22:06 -04:00
cproudlock
bc89ba1cf3 CMM: add Register-PCDMIS-COM.bat desktop helper for post-license COM regserver
PC-DMIS COM must be registered for goCMM to connect, but Pcdlrn.exe /regserver
no-ops until PC-DMIS is licensed - which is a manual post-image step
(clmadmin.exe). So we cannot register at imaging time. Instead 09-Setup-CMM
drops a self-elevating one-click helper on the SupportUser desktop (a
pre-existing profile; avoids 06-OrganizeDesktop's Public-desktop sweep). The
tech runs it after activating the license; safe to re-run. Falls back to Public
Desktop if the SupportUser profile is absent. Copy happens before the Step 3
staging cleanup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 17:10:44 -04:00
cproudlock
1487abdba5 CMM/DODA: enable on bays 4/10/11/12, fix PCTypes match, grant PreProcess ACL
Three fixes so DODA actually deploys on the four bays that need it:

- cmm-bay-config.csv: doda=yes for CMM4, CMM10, CMM11, CMM12 (was no on all
  bays). Drives doda.txt -> startnet pc-subtype.txt=doda -> the cmm-doda path.
- cmm-manifest.json: DODA entry PCTypes "cmm-doda" -> "gea-shopfloor-cmm-doda".
  The old value never matched: Test-PCTypeMatches builds the PC identity set as
  {gea-shopfloor-cmm, gea-shopfloor-cmm-doda, CMM} (the alias of the cmm type is
  bare CMM, no subtype variant), so "cmm-doda" was in no set and the entry was
  silently skipped. Must be the exact Type-SubType string.
- Install-DODA.ps1: grant Users + Authenticated Users Full on C:\Apps\DODA\
  PreProcess (icacls, SIDs, OI/CI inherit), applied every run. MergeFiles.exe
  writes there as the locked-down operator; without it the merge step fails.

Not yet pushed to the SFLD/enrollment share (server unreachable at commit time).
Not smoke-tested - no pwsh available here; logic-traced only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 16:35:39 -04:00
cproudlock
7af66575d0 CMM: gocmm-debug - capture the real startup NRE (PC-DMIS COM not registered)
debug.log from the bay shows the part group enumerates fine (14 .geop files
opened), then goCMM connects to PC-DMIS over COM and throws:

  System.ArgumentNullException: Value cannot be null. Parameter name: type
     at System.Activator.CreateInstance(Type type)
     at GEAE.Common.CMM.CMMInterfaces.PCDMIS.PCDMIS.<ConnectToSoftware>b__43_0()
  -> System.NullReferenceException at ConnectToSoftware(...)

Root cause: PC-DMIS automation server is not COM-registered, so
Type.GetTypeFromProgID returns null -> CreateInstance(null) throws -> the NRE
is the downstream symptom. Not part-group, not permissions, not calibration.

- PROBE 4: pull the .NET Runtime / Application Error crash stack for goCMM from
  the Application log so the next run captures the null in one shot.
- PROBE 5: (a) part-group UNC reachability; (b) PC-DMIS COM registration check
  (PCDLRN.* ProgID -> CLSID -> LocalServer32) that names the missing registration
  and the Pcdlrn.exe /regserver fix; plus install presence for context.
- .bat header documents the COM root cause.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 08:31:11 -04:00
cproudlock
8e11f0824a CMM: gocmm-debug probes the startup NRE (part-group match), checks real exe
goCMM matches the registry 'Selected Part Group' against the
ApplicationSettings.xml <PartGroup FullName> entries with a case-sensitive
compare. No match -> SelectedPartGroup null -> "Object reference not set to
an instance of an object" at start. This is a different failure from the
registry SecurityException the script already probes.

- PROBE 3: read the reg value (32-bit view) + every FullName in the XML,
  Ordinal-compare, and report exact / case-only / no-match / missing-XML.
  Case-only and no-match name the d441abd canonicalization fix as the remedy.
- Version check now looks for the real goCMM.exe, not the nonexistent
  GEAOperatorFriendlyInterface.exe (was a false MISSING).
- .bat header documents both failure modes it now diagnoses.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 08:08:00 -04:00
cproudlock
d441abd20f CMM: goCMM restore - canonicalize the SHARE segment case, not just the host
goCMM showed an empty parts list after restore though the bay reached the share.
Decompiled goCMM: PartGroupViewModel matches the registry Selected Part Group
against ApplicationSettings.xml <PartGroup FullName> with a CASE-SENSITIVE compare,
then enumerates that FullName for the parts. The host-canon rewrite fixed only the
hostname, leaving xml '\shared' (lowercase) vs registry '\SHARED' (uppercase) ->
Find null -> SelectedPartGroup null -> empty list. Fix spans the share segment too,
pinning both to \tsgwp00525.wjs.geaerospace.net\SHARED. Verified in PowerShell
(-ceq True). Runs at imaging in Restore-CMM, so all captured backups are fixed on
restore with no re-backup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 13:12:07 -04:00
cproudlock
9145023440 heal/fetch: suppress benign net-use-delete error during imaging
The pre-mount `net use Z: /delete /y` in Fetch-StagingPayload and
Verify-And-Heal-Staging emits "The network connection could not be found" when
Z: is not yet mapped (the normal first-attempt case). PowerShell surfaces that
native stderr as a NativeCommandError (System.Management.Automation.Remote-
Exception) at the call site EVEN WITH `2>$null` - it prints a red error during
the FirstLogonCommands run, alarming the tech and able to mask a real fault.
The mount then succeeds, so it was always cosmetic.

Wrap the cleanup in cmd.exe (`cmd /c "net use $drive /delete /y >/dev/null 2>&1"`) so
net.exe's stderr is redirected to nul INSIDE cmd and never reaches PowerShell as
an error record. Verified on the win11 VM: old pattern leaves $Error.Count=4
(RemoteException); new pattern leaves $Error.Count=0. All four call sites fixed
(both scripts' Mount-Share + end-of-run unmount).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 11:50:27 -04:00
cproudlock
51edf98e7d heal: verify file completeness (size/timestamp), not just presence
The shallow present-check passed a file that merely existed, so a partially
transferred payload (e.g. a truncated PC-DMIS MSI) looked PRESENT and was never
re-pulled - then failed to install because it was incomplete. Replace it with a
per-item robocopy that compares size + timestamp on every file and re-pulls
anything missing OR partial, skipping ones already complete. VerifyOnly uses /L
to report INCOMPLETE without changing anything.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 11:20:32 -04:00
cproudlock
e3a3fa6794 startnet: stage Verify-And-Heal-Staging.ps1 to C:\Enrollment
The live boot.wim startnet did not stage Fetch-StagingPayload.ps1 (rebuilt from a
stale source), so the unattend Fetch (Order 4) + Verify-And-Heal (Order 5) steps
had no script on disk and never ran - imaging lost payloads with no recovery.

Stage Verify-And-Heal-Staging.ps1 directly here (alongside the existing
Fetch-StagingPayload copy) so the Order 5 heal runs even if Fetch itself fails to
land. Requires re-injecting this startnet.cmd into boot.wim to take effect.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 10:31:32 -04:00
cproudlock
a165a79f95 imaging: force shopfloor unattend deploy (was force:no -> went stale)
The shopfloor unattend deploy used force:no, so once a live copy existed the
playbook never overwrote it. That let the live gea-shopfloor unattend drift for
weeks - missing the Fetch + Verify-And-Heal staging steps - which is why imaging
lost payloads (CMM bundle/backups). Flip to force:yes so the repo stays the
source of truth, matching the standard/engineer unattend task.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 10:12:06 -04:00
cproudlock
41cace17e8 imaging: run Verify-And-Heal-Staging at first logon before the network switch
Wire the staging self-heal into the imaging flow so a bay re-pulls any missing
payload while still on the imaging LAN (172.16.9.1), before wait-for-internet
takes it to the production network.

- FlatUnattendW10-shopfloor.xml: insert Verify-And-Heal-Staging.ps1 as
  FirstLogonCommands Order 5 (right after Fetch-StagingPayload Order 4, before
  wait-for-internet); renumber the rest 6-10. Run-ShopfloorSetup stays last and
  is NOT the heal point - it runs post-network-switch when the imaging LAN is
  gone.
- Fetch-StagingPayload.ps1: also pull the small Verify-And-Heal-Staging.ps1 to
  C:\Enrollment so the Order 5 step has it on disk.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 10:01:59 -04:00
cproudlock
e97e5bd049 shopfloor: CMM PC-DMIS version gate, ShopDB reporter fixes, staging self-heal
- lib Install-FromManifest 2.5->2.6: add _CmmVersion per-entry filter (reads
  C:\Enrollment\cmm\version.txt). Lifted the version gate out of 09-Setup-CMM
  into the shared lib so imaging and GE-Enforce apply it identically and cannot
  drift (root cause of PC-DMIS 2016 installing on every CMM).
- Install-goCMMSettings: canonicalize the part-group share host to the FQDN in
  both the registry and ApplicationSettings.xml. Handles bare \\tsgwp00525\ and
  the legacy rd.ds.ge.com domain; idempotent. VM-tested.
- Report-AssetToShopDB: resolve the machine number eDNC registry first, then fall
  back to C:\Enrollment\machine-number.txt (matches the lib resolution order) so
  a freshly imaged PC still reports its number for the PC-machine relationship.
- Add Update-CMMEnforcer.ps1/.bat: update one CMM's local lib to the gated
  version and self-heal its PC-DMIS version.
- Add Debug-ShopDBReporting.ps1/.bat: one-shot reporter triage (preconditions,
  client log, live test POST, verdict).
- Add Verify-And-Heal-Staging.ps1/.bat: post-boot check that every imaging
  payload arrived and re-pull anything missing from the share, including the CMM
  bundle and the selected bay's backup (the payload that times out in WinPE).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 09:14:54 -04:00
cproudlock
c2538a05c5 CMM: wire per-bay settings restore into the imaging flow
Restore-CMM.ps1 (new) restores a CMM's PC-DMIS + goCMM settings at imaging from
its staged backup. Self-gating: reads C:\Enrollment\cmm\{cmmid,version,doda,
partgroup}.txt, skips DODA bays and bays with no staged backup, and restores
ONLY the config-version PC-DMIS zip via the existing Install-*Settings scripts.
Same-bay restore (cmmid match) so the backed-up controller CommPort is this
bay's own value - no cross-bay clobber.

Version selection matches the VERSION FIELD of the zip name, anchored on the
trailing timestamp, so version=2026 does not false-match a 2019/2016 zip whose
backup timestamp (20260612...) merely contains "2026".

09-Setup-CMM.ps1: new Step 2.8 calls Restore-CMM after app install + first-run
init (so a restored config is not clobbered by PC-DMIS defaults) and before the
C:\CMM-Install cleanup (the backup set lives under <stagingRoot>\backups\<cmmid>).
Best-effort: Restore-CMM always exits 0, imaging never fails on a restore.

startnet.cmd: stage ONLY the picked bay's backup into C:\CMM-Install\backups\
%CMMID% (the bulk robocopy now /XD-excludes the backups tree, which holds every
bay's backup - some 240 MB each - to avoid copying GBs to every imaged CMM).
Also bump the PPKG to v4.16 (the live boot.wim was already v4.16; the repo had
drifted to v4.14).

sync-cmm-backups.sh: source the backups from pxe-images/cmm/backups (where
Backup-CMM writes via the pulled-down copies), not the old cmm-bk path.

Smoke tested on the win11 VM against CMM3's real backup: version=2019 restored
the 2019 R2 zip (not 2016.0), imported HKLM+HKCU reg, converted the part-group
S:\ path to the tsgwp00525 UNC, created C:\geaofi, exit 0; version=2026 correctly
found no matching zip (anchor works).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 15:34:10 -04:00
cproudlock
59deaea714 CMM: Backup-CMM defaults to S: record-retention path + prompts for CMM#
Default output is now S:\2 WJ Scans Record Retention\backup\cmm\<CmmId>\ instead
of C:\Logs. If S: is not mapped/reachable it falls back to C:\Logs\CMM\cmm-backup
so the backup still runs. When -CmmId is not passed the script prompts for it
(loops until non-empty) since it names the per-bay folder.

Smoke tested on the win11 VM: S: fallback path + the Read-Host prompt (fed via
redirected stdin) both produce the correctly-named per-CMM folder.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 15:34:10 -04:00
cproudlock
f49fa0f940 CMM: install PC-DMIS PDF converter (Amyuni) the standalone-MSI bypassed
Our patched standalone PC-DMIS MSI never installs the Amyuni Document
Converter 500 (system printer "PC-DMIS 50 Converter"). INSTALLPDFCONVERTER
is a Burn-bundle property the main MSI never reads (0 of 153 custom actions
reference it; not in the Property table), and the patched-MSI strategy
bypasses the bundle that would have chained the Amyuni install. The MSI only
lays the installer on disk at <installdir>\PDFDriverInstallFiles\
BatFileInstallPDF50.zip and nothing runs it.

Install-PCDMISPDFConverter.ps1 runs it: scans Program Files\Hexagon (and
Wai) for the laid-down zip, extracts it, parses the InstallPDF50.exe
invocation from the shipped bat (printer name + Wilcox licensee + license
code, read not hardcoded), and runs the exe directly from the extract dir
so sibling DLLs resolve. The shipped bat ends in `pause` (hangs under /qn)
so we never run it. InstallPDF50.exe creates the printer then hangs (same
trait as the bundle), so we poll for the printer and kill the stuck exe
once it appears. Idempotent: printer already present -> exit 0.

Wired as a PS1 manifest entry placed after the PC-DMIS MSIs (files must
exist on disk first), no _CmmVersion (one shared printer covers every
version), MarkerFile detection for one-shot at imaging.

Smoke tested on the win11 VM as SYSTEM: fresh install 7.2s (printer +
driver created), idempotent re-run 0.6s, both exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 14:10:54 -04:00
cproudlock
c8e704b595 CMM: add backup staging (sync-cmm-backups.sh) + write cmmid.txt at resolve
sync-cmm-backups.sh pushes per-CMM backup sets (goCMM + PC-DMIS zips produced
by Backup-CMM) from pxe-images/cmm-bk/<cmm_id>/ to the PXE share at
installers-post/cmm/backups/<cmm_id>/, atomic-swap with a timestamped prior
copy. Distinct from sync-cmm.sh (which stages the CMM installer bundle).

resolve-cmm-bay-config.ps1 now also writes cmmid.txt alongside version/doda/
partgroup, so 09-Setup-CMM can locate this bay's staged backup for
restore-by-machine-number.

The 09-Setup-CMM restore block + startnet staging line are intentionally NOT
added yet - the restore needs manual end-to-end validation on a real CMM
before auto-running at imaging (per the live-bay restore issues we hit).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 08:47:21 -04:00
cproudlock
1d65103cc0 CMM: add PC-DMIS + combined CMM backup/restore + diagnostic scripts
Adds the PC-DMIS settings/probe backup-restore set alongside the existing
goCMM scripts, plus a single combined CMM backup and the diagnostics built
while debugging the live bays:

- Backup-PCDMISSettings / Install-PCDMISSettings: capture+restore PC-DMIS
  registry + data/probe/cal files per installed version (2016/2019/2026).
  Hardened from real-bay failures: detect install dir via Program Files
  fallback; capture compens.dat (not just comp.dat) + interfac.dll; identify
  the controller by hash-matching interfac.dll to its source DLL AND reading
  the PE OriginalFilename (covers rename-without-copy); EXCLUDE the whole
  Homepage state (Recent/Favorites/DetailsView) which null-refs PC-DMIS on
  launch via stale routine paths; restore routes HKCU into the target user's
  hive (-TargetUser ShopFloor), fails loud on a non-backup path, and applies
  the legacy->new FQDN rewrite across reg + data files incl .bas.
- Backup-CMM: one wrapper running goCMM + PC-DMIS (all versions) into one
  per-CMM folder + index, for staging on PXE and restore-by-machine-number.
- Clear-PCDMISRecent: fixes the Homepage recent-list NullReferenceException
  crash on an already-broken bay.
- pcdmis-probe-debug / Export-PCDMISCrashEvents: diagnostics for the
  custom-probe-not-showing and crash investigations.
- Modify-PCDMISRights / Grant-FullControl: grant the operator the registry +
  filesystem access PC-DMIS needs under lockdown.
- Install-goCMMSettings: add .bas to the FQDN-rewrite include list.

Not yet wired into 09-Setup-CMM auto-restore - staging + the gated restore
block come next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 08:42:32 -04:00
cproudlock
bfe17fe123 CMM: add goCMM settings backup/restore + debug scripts
goCMM settings live in two places: 3 pointer values at
HKLM\SOFTWARE\WOW6432Node\General Electric\goCMM, and the real content of all
7 Settings tabs (PC-DMIS, Quindos, Modus, Machine Definition, User Input,
Notifications, Part Groups) in C:\geaofi\ApplicationSettings.xml. Capture-replay
pair, mirroring the Wax/Trace Backup/Install scripts:

- Backup-goCMMSettings.ps1/.bat: on a live legacy bay (admin), zips the registry
  key + the C:\geaofi tree (minus transient LocalProgramCopies/logs) to
  gocmm_backup_<PC>_<ts>.zip.
- Install-goCMMSettings.ps1/.bat: restore at imaging (admin). Imports the key +
  lays down C:\geaofi, then grants BUILTIN\Users WriteKey on the reg key and
  Modify on C:\geaofi - goCMM's RegistrySettings.GetRegistryString opens the key
  with writable:true even to READ, so a locked-down operator throws a
  SecurityException without the grant (the post-lockdown 'registry access not
  allowed' error). Applies a built-in legacy->new FQDN rewrite
  (rd.ds.ge.com -> wjs.geaerospace.net) automatically across the registry values
  and ApplicationSettings.xml (incl PartGroup FullName); -NoDefaultRewrite skips
  it, /replace adds an extra pair, -SelectedPartGroup overrides per bay.
- gocmm-debug.ps1/.bat: run as the operator to reproduce the SecurityException
  and dump the goCMM key ACL (confirms whether lockdown stripped the grant).

All round-trip + FQDN-rewrite verified on the win11 VM. NOTE: covers goCMM only;
PC-DMIS probe calibrations / custom tip angles / machine comp are owned by
PC-DMIS (Hexagon) and not captured here. Not yet wired into 09-Setup-CMM
auto-discovery.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 08:27:30 -04:00
cproudlock
da380fbcd7 Wax/Trace: prefer backup ZIP over cal ISO when both exist
Standardize the calibration source. Step 3b already restores a per-asset
backup ZIP (HKLM + data + config, incl. the probe cal tables) for any bay
that has one - which is now every migrated bay. When that ZIP is staged,
skip the redundant and fragile cal-ISO mount/vendor-Setup step so all
bays follow one flow (app ISO + backup restore), matching the bays that
never had a cal ISO. A future bay with a cal ISO but no backup still
falls through to the ISO path unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 17:13:03 -04:00
cproudlock
a935c04e1f Wax/Trace WJF00450: set user_id, pin 6.213 app-ISO choice
Fill in user_id 0992830128 (was a MISSING_DATA placeholder pending a
dongle read). Keep ftpak_version 6.213: the bay runs FormTracePak
6.2.0.45 but no V6.204 app ISO exists, so it installs the V6.213 app and
restores the 6.2.0.45 config/cal from its per-asset backup ZIP - the same
pattern as WJF00461 (6.213 ISO / 6.2.0.51 backup). Cal .txt tables are
version-independent so they apply onto the 6.213 binaries. hw_id stays
MISSING_DATA (metadata, not consumed by the resolver). Sync the scripts/
copy so the resolver and the backup tools see identical data.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 17:09:30 -04:00
cproudlock
1e17a0564f eDNC: standardize per-type fallback installers on 6.4.7
Replace per-type eDNC_6-4-5.msi with eDNC-6.4.7.msi (ProductVersion
6.4.7.0) in collections, nocollections, heattreat, and partmarker.
01-eDNC.ps1 already prefers the single shared installer at
C:\PreInstall\installers\dnc (6.4.7); aligning the per-type fallback
means an empty shared dir can no longer install a stale 6.4.5. All DNC
types now resolve to 6.4.7 on either path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 17:09:19 -04:00
cproudlock
99e7679e87 HeatTreat: bump DNC app 6.2.1 -> 6.4.9
Replace HeatTreat_6.2.1.msi with HeatTreat_6-4-9.msi (ProductVersion
6.4.9.0, ProductCode {9E603EFE-888A-4E3F-8CF5-7F03B7029919}). The install
script globs HeatTreat*.msi so no logic change; the MSI's NOT
NEWERVERSIONDETECTED LaunchCondition makes 6.2.1 -> 6.4.9 a clean
in-place upgrade. Update version references in 02-Setup-HeatTreat.ps1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 17:09:05 -04:00
cproudlock
1e6603d331 eDNC: default to single shared installer (C:\PreInstall\installers\dnc\eDNC-6.4.7.msi)
01-eDNC.ps1 (all 4 DNC types) now installs eDNC from the shared pre-install copy
staged by startnet.cmd to C:\PreInstall\installers\dnc - one source of truth
(currently 6.4.7) instead of a per-type bundled msi. Falls back to the per-type
eDNC\ folder when the shared copy is absent (older images). eMxInfo.txt, the
x86->64 mirror, and Site/MachineNo registry steps are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 10:45:24 -04:00
cproudlock
e844ff367c site-config: common shortcut base on all shopfloor types + per-type apps; new homepage host
- Homepage/start-menu + Edge URLs: http://tsgwp00524.logon.ds.ge.com/ ->
  https://tsgwp00525.wjs.geaerospace.net (8 refs across site-config + 08-EdgeDefaultBrowser).
- Every shopfloor working type now carries the same common base: Edge + WJ
  Shopfloor + Defect_Tracker pins/desktop, WJ Shopfloor + Plant Apps startup,
  3 web tabs. Timeclock/Genspect/Keyence/CMM upgraded to match.
- Per-type specialized apps: CMM (PC-DMIS 2016/2019 R2, CLM Admin, goCMM),
  WaxAndTrace (Formtracepak), partmarker (Mark + Backup420 only - dropped
  Telesis Backup400/470), heattreat (HeatTreat).
- eDNC/UDC/NTLARS listed only on bays that can run them; 06-OrganizeDesktop +
  07-TaskbarLayout gate every exe entry on Test-Path, so uninstalled apps are
  silently skipped (the "eDNC if it exists / UDC if it exists" behavior).
- Lab + Display-* left as kiosk/lab, not given the shopfloor base.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 08:08:32 -04:00
cproudlock
913c807142 preinstall: OpenText + serial drivers on all PC types; Oracle on DNC bays + CMM
- Shopfloor Serial Drivers: PCTypes ["Standard"] -> ["*"]. Serial hardware
  appears across bays; a driver where the hardware is absent is harmless (sits
  in the driver store). PartMarker/HeatTreat were getting skipped.
- OpenText HostExplorer ShopFloor: PCTypes -> ["*"]. Every shopfloor PC type
  should get HostExplorer + its profiles/EB/keymaps/menus + desktop shortcuts.
- Oracle Client 11.2: scoped to the DNC-bearing types (collections,
  nocollections, partmarker, heattreat) + CMM (metrology tooling links the
  Oracle home). Dropped Genspect/Keyence/WaxAndTrace/Display/Timeclock, which
  have no DNC and don't need the Oracle client.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 07:40:11 -04:00
cproudlock
1853db0903 preinstall: run OpenText on Part Marker bays (profiles, EB macros, desktop shortcuts)
OpenText HostExplorer was skipped on Part Marker PCs because its PCTypes
filter in preinstall.json omitted PartMarker, so Setup-OpenText.ps1 never
ran - no MSI, no per-user profile/keymap/menu/macro fan-out, no .eb macros
into ProgramData\Hummingbird\Connectivity\15.00\Shared, and no public
desktop shortcuts.

- preinstall.json: add PartMarker + gea-shopfloor-partmarker to OpenText PCTypes
- 00-PreInstall-MachineApps.ps1: add PartMarker alias group to the preinstall
  matcher (parity with Install-FromManifest.ps1, which already had it)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 18:49:36 -04:00
cproudlock
7a67716fcc manifest engine: null-safe PS1 dispatch (accept Script or Installer, log resolved path)
PS1 entries were crashing with a cryptic "Cannot bind LiteralPath because it is
null" when the resolved script path came back null - the per-entry try/catch
caught it (so the scope survived) but the cause was opaque. Now the PS1 branch
accepts either Script or Installer, null-guards before Join-Path/Test-Path, and
logs the resolved relative path, so a bad/empty entry is skipped with a clear
"has no Script/Installer value" line instead of a null-bind throw.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 17:38:16 -04:00
cproudlock
995909042b preinstall: install Oracle Client 11.2 on Part Marker + HeatTreat bays
The preinstall Oracle entry (run off the PXE pre-install share during shopfloor
setup, NOT the runtime enforcer) gated PCTypes to the old taxonomy only -
Standard/CMM/Genspect/Keyence/WaxAndTrace/Display - so gea-shopfloor-partmarker
and gea-shopfloor-heattreat bays were pc-filtered and never installed Oracle.
These two pctypes were added after the rename reorg and only exist as
gea-shopfloor-* names, so they were missing from the legacy-named list. Added
both. Already deployed live to the PXE enrollment share's preinstall.json.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 17:04:17 -04:00
cproudlock
a351160520 manifest engine: reg-first machine number + per-app crash isolation; add start-layout DSC
Install-FromManifest.ps1:
- Get-CurrentMachineNumber reads the eDNC/DNC registry FIRST (reassignment-
  authoritative), falling back to C:\Enrollment\machine-number.txt. The txt is
  written once at imaging and is NOT updated on reassignment, so txt-first
  gated reassigned bays on a stale number.
- Per-entry try/catch in the app loop: a single entry that throws no longer
  aborts the whole scope (skipping every later entry + the status write). It is
  logged, counted failed, and the loop continues. This was silently killing the
  collections scope at the MTConnect Makino entry, which also stopped the
  ShopDB asset reporter (a later entry) from ever running.

Deploy-ShopfloorStartLayout.ps1 (new): local-DSC port of the Intune
desktop-weblinks + Start-menu pins (copies .url/.lnk to Public Desktop +
All-Users Start Menu, writes the ConfigureStartPins JSON policy, resets
start2.bin + restarts the shell). Verified on Win11: pins render after logon.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 16:42:40 -04:00
cproudlock
a380b17112 collections: report host + IP + machine number to ShopDB each enforce cycle
Adds Report-AssetToShopDB.ps1 (Type=PS1, DetectionMethod=Always manifest entry)
for collections PCs. Reads hostname, BIOS serial, eDNC MachineNo and the corp
NIC IPv4 (filtered to WJ corp ranges, controller NIC dropped) and POSTs
action=updateCompleteAsset to ShopDB api.asp, which upserts the machine, stores
the IP, and links the PC to its machine-number equipment. manifest-entry-report-asset.json
is the snippet to merge into the SFLD share collections manifest (+ stage the
script under apps/). Note: relies on the ShopDB api.asp LogToFile Err-leak fix
(separate shopdb repo commit) to create the relationship reliably.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:04:33 -04:00
cproudlock
5211861409 manifest: add PartMarker pctype alias so common entries reach Part Marker PCs
PartMarker was missing from the PCType alias map, so gea-shopfloor-partmarker
matched no alias set and common manifest entries gated by PCTypes (notably
Oracle Client 11.2) were pc-filtered out - Part Marker PCs never installed
Oracle. Adds @('PartMarker','gea-shopfloor-partmarker') to the alias groups.
The companion fix (adding partmarker+heattreat to Oracle's PCTypes list) lives
in the SFLD share common/manifest.json. Verified on the win11 VM: with PCType
gea-shopfloor-partmarker the Oracle entry is now evaluated (0 pc-filtered).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:04:33 -04:00
cproudlock
db6be99d43 samba: deadtime=0 (was 5) so WIM-apply idle does not drop the staging mount
WinPE maps the enrollment share early, then idles for minutes while the WIM
applies. deadtime=5 dropped that idle session, so the post-apply staging
copies failed (bay left with only site-config.json staged). VM test against
the live share copied everything fine on a fresh mount; the only difference
was the idle. deadtime=0 disables idle auto-disconnect. Applied live to the
current box (smbd reloaded); this makes it permanent + covers the second box
on the next playbook run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 09:04:37 -04:00
cproudlock
a6fa21589b Imaging: defer bulk staging to first-logon Fetch (fresh mount) - Phase 1
WinPE maps Y: early then idles for minutes during the WIM apply; samba
deadtime drops the idle session, so the WinPE staging copies failed (bay
left with only site-config.json). Add Fetch-StagingPayload.ps1, run from the
unattend FirstLogonCommands at first logon on a FRESH share mount (full
Windows, no prior idle), to pull the shopfloor-setup tree + preinstall
bundle. Detailed per-item log (exit code, counts, timing, mount retries) at
C:\Logs\Fetch\ - the old WinPE staging was opaque.

- Fetch runs as Order 4, BEFORE wait-for-internet.ps1 (Order 5) which switches
  the bay to the production network and off the imaging LAN. So Fetch still
  reaches \172.16.9.1\enrollment.
- WinPE bulk staging kept as best-effort fail-fast fallback (Phase 1); the
  post-boot Fetch is now the authoritative path. Remove the WinPE bulk once
  validated. Heavy per-type payloads (CMM/Keyence/WaxTrace) stay in WinPE for
  now - Phase 2.
- startnet stages Fetch-StagingPayload.ps1 + writes fetch-source.txt
  (UNC/user/pass) for the post-boot mount.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 09:01:19 -04:00
cproudlock
f570e92847 startnet: robocopy (fail-fast) for staging copies so a stalled Y: cannot hang imaging
A Display bay staged only site-config.json then nothing - winpe-staging.log
ended at 'Copied site-config.json'. PCTYPE/PPKG were both set, so it was not
a menu bug: execution froze at the very next step, the PPKG copy
  copy /Y Y:\ppkgs\... W:\Enrollment\...
A bare 'copy' from a network drive has no timeout; when the Y: SMB handle
stalled it blocked forever, so pc-type.txt / shopfloor-setup / preinstall
never ran (Display kiosk + Common OpenText both depend on the preinstall
phase). Convert every bare 'copy /Y' in the staging path to
'robocopy /R:1 /W:1' (fail-fast retry, no overwrite/dir prompts). robocopy
exit codes are inverse of copy (0-7 ok, 8+ fail) so the PPKG check flips to
errorlevel 8, and since robocopy keeps the source name the PPKG gets a ren
to its BPRT-tagged target. A stalled share now fails one step and continues
instead of freezing the whole stage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 16:21:39 -04:00