Set-MachineNumber rewrote one Devices.xml per variant and restarted the agent.
That is right for Okuma and eDNC, which keep the device name in that one file -
checked across 7 Okuma and 15 eDNC bay captures. Fanuc and Makino keep it in
several, and editing only the agent's copy leaves the adapter streaming under
the old identity.
It has already happened. Seven of thirty-four captured Fanuc bays have the
agent on the right machine number and the adapter still on the bay it was
imaged from: 4007 on a 7801 Toshulin, 3031 on a 7804, 2005 against 2006 on a
dual-spindle pair. Both captured Makino bays have Devices.xml on the machine
number and the other three files still on MAKINO-1.
Update-MTConnectVariantName renames across a declared set of files with the
owning services stopped first. Makino needs that ordering more than most: the
vendor's guide says the Adapter Manager rewrites the COMPLETE configuration
from memory as it stops, so an edit made while it runs is discarded, and that
the name must be identical in every file or the agent may not start.
It collects every name in play before rewriting rather than discovering one.
A bay half-renamed by the old code carries two at once, and converging on the
one that happened to be found leaves the other behind - which is the state this
is meant to end, not reproduce. Nothing is written if a service will not stop,
and whatever was stopped is started again.
Tested on Windows 11 against copies of the real captures: the 7801 bay (adapter
4007) converges to 7801 in two files and leaves the correct agent alone; the
7502 Makino bay converges all four; second runs report no changes. Service
ordering verified by process id, using stand-in services - sc.exe fakes named
after the real ones are not real services, so the service list is a parameter
defaulting to the production names.
Does not fix the bays already in this state; they need a run each.
Sixteen files that run on every shopfloor PC existed only on the SFLD share.
The cost showed up while debugging the NTLARS backup: the script that posts to
ShopDB could not be read, reviewed or diffed, so its behaviour was inferred
from log output for most of a day. It turned out to hold a silent fallback that
had been governing the whole fleet for months.
Imported as-is from tsgwp00525-v2, no edits:
lib/ShopdbBackupClient.psm1 the shared backup client
scripts/Backup-NtlarsSettings.ps1 converted to use it
scripts/Set-ShopdbCollectorKey.ps1 collector credential delivery
scripts/Test-RegExport.ps1 exercises the .reg codec with mocks
scripts/Set-EventSaver*.ps1 kiosk power / screensaver / disable
scripts/Setup-OpenText.* OpenText install + toolbar
scripts/Migrate-PCType.ps1, Select-KioskType.ps1, Set-FmsHostsEntry.ps1,
scripts/ensure-vnc-firewall.ps1, Install-AcroReader.cmd, Install-Oracle11r2.cmd
lib/Install-FromManifest.ps1 is also updated from the share, which was 37 lines
AHEAD of this repo and purely additive: the Add-EnforceResult reporting added
during the kiosk API cutover, done live and never committed back. Nothing was
removed.
Checked for embedded secrets before committing; there are none.
Set-ShopdbCollectorKey deliberately reads its token from a sibling file on the
share rather than holding it, so the script is safe to track.
The share remains what actually runs. This makes it reviewable, and makes the
next drift visible as a diff rather than a surprise.
The prune drops a *.log by LastWriteTime. A log written to a fixed filename is
appended every cycle, so its LastWriteTime is always now, it can never be older
than any cutoff, and it grows without limit. ntlars-backup.log, eventsaver.log
and shopdb-collector-key.log all do this. On one part-marker PC eventsaver.log
had reached 11,000 lines and ntlars-backup.log 3,234, of which 3,217 were the
same "Throttled" line - the four events that mattered were unfindable without
grep -v.
Undated logs are now rolled to <name>-YYYYMMDD.log before the prune runs, which
stops them being written to and lets them age out on the existing 30 day
retention. The owning scripts need no change: they append with Add-Content or
Tee-Object, which recreate a missing file on the next write. This matters
because those three scripts are live-share artifacts, not in this repo.
Rolled under the date of its FIRST LINE, so the stamp matches the contents.
That is also the only trustworthy signal: CreationTime is not, because NTFS
file tunneling gives a recreated file the old creation time when it reappears
within 15 seconds, so a busy log keyed on that would look stale the instant it
rolled and would roll again every cycle.
A log with no parseable timestamp is left alone until it passes 5MB, so an
unrecognised format still cannot grow forever. Empty files and already-stamped
files are skipped. Rolling onto an existing target appends rather than
overwriting, so a second roll on one day loses neither side.
Verified against real files under pwsh, including the append branch and the
owner recreating the file afterwards. The first attempt keyed on CreationTime
and a second used [datetime]::TryParse with an untyped $null, which throws "no
overload" and would have made the whole roll a silent no-op; the ref is now
pre-typed.
The post-fix capture from 579C144 reported three entries as unknown(0x04) -
SecurityHealth, RtkAudUService, WavesSvc. The decoder only knew 02/06 enabled and
03/07 disabled. 04 is also enabled and 05 also disabled; without them the report
says 'unknown' for entries that are perfectly ordinary.
PREINSTALL SCOPE
Sixteen entries carried PCTypes ['*'], so a Display kiosk installed Adobe
Acrobat, OpenText HostExplorer, WJF Defect Tracker, the shopfloor serial drivers
and twelve legacy VC++ redistributables. Those are now scoped to the nine
machine-tool types.
A Display now installs 3 of 21 entries:
PowerShell 7.5.4 SFLD-ApplyDSCConfig runs pwsh.exe 7
VC++ Redistributable 2015-2022 x64 modern x64 runtime Edge/WebView2/PS7 lean on
Display Kiosk App already Display-scoped
Kept as wildcards deliberately - removing the x64 runtime risks breaking the
kiosk itself, and PS7 is a hard dependency of the DSC task. Everything else a
kiosk has no use for. Edited as targeted text replacement, 16 line pairs, so the
file's formatting and its long _comment blocks are untouched.
THE LAST OF THE EMPTY-MEANS-DEFAULT BUGS
Configure-PC.ps1 and 07-TaskbarLayout.ps1 had the same test as the two fixed
earlier: $null -ne $cfg -AND .Count -gt 0, so an explicitly empty list fell
through to a hardcoded shopfloor default. Configure-PC is the one that actually
bit - it runs in the finalization phase and put these in the all-users Startup
folder on 579C144, timestamped mid-image:
Defect Tracker.lnk 16:46:03
Plant Apps.lnk 16:46:03
Plant Apps launching msedge --new-window on a kiosk. eDNC was in the same
hardcoded list and only escaped because its Test-Path guard found no DncMain.exe.
08-EdgeDefaultBrowser.ps1 had it too, with a Plant Apps + WJ Shopfloor + Dashboard
tab fallback. Harmless today because the Display profiles configure a real tab,
but one edit away from biting.
Get-ProfileValue returns $null only when a key is absent from BOTH the profile
and site-config, so $null is the only honest "not configured" signal and an
empty array means what it says.
VERIFIED against the post-fix capture: the S: mapper Run key and the machine
number prompt task are both gone, and 'ShopDB Kiosk Bootstrap' registered
correctly with boot and time triggers.
Also: the collector's StartupApproved decoder now handles 0x04/0x05, which that
same capture surfaced as unknown.
Still baked into the WIM and unaffected by any of this: WJ Shopfloor.lnk, dated
April, sitting in the all-users Startup folder.
Three fixes from the 579C144 diagnostics, all the same shape: a Display kiosk
being treated as a machine-tool bay.
desktopApps: configuring "none" produced "everything"
"Display-Dashboard": { "desktopApps": [] }
06-OrganizeDesktop.ps1 tested $null -ne $cfgApps -AND .Count -gt 0, so an
explicitly empty list fell through to the hardcoded fallback and the kiosk was
given UDC, eDNC, NTLARS, WJ Shopfloor and Defect_Tracker shortcuts.
Get-ProfileValue returns $null only when the key is ABSENT from both the profile
and site-config, so $null is the real "not configured" signal and an empty array
means what it says. startupItems carried the same test - harmless today because
its else branch has no fallback list, but commented so the two cannot drift.
Machine-number prompt on a machine with no machine number
Register-CheckMachineNumberTask.ps1 armed 'Prompt Machine Number' (AtLogOn,
BUILTIN\Users) whenever the number was the 9999 placeholder - which is always
true on a Display, because startnet only collects a real number for machine-tool
types. A logon dialog on a kiosk with no keyboard. Now skips PC types that have
no machine number by design, and clears any stale task.
S: mapper on a share-less PC
Displays are Entra-joined with local accounts and no SFLD credentials, so mapping
S: can only fail, once per logon, forever. Run-ShopfloorSetup already gated the
CALL on $noEnforceTypes, but the bay registered it anyway at 15:07:55 with no
"Skipping" line in the log - so something in the finalization phase reaches the
registrar past that gate. Rather than chase the caller, the registrar now gates
itself and removes a stale Run entry. The call-site gate stays; this makes the
outcome correct regardless of who invokes it.
That bypass is worth understanding separately - the same pattern would defeat any
call-site gate in the finalization phase.
All three parse clean and are deployed byte-identical to the share.
The bootstrap looked for C:\Enrollment\kiosk\Install-ShopdbKiosk.ps1, but
startnet copies the type-specific tree to
C:\Enrollment\shopfloor-setup\gea-shopfloor-display\ - so the payload lands
beside the bootstrap, not under C:\Enrollment.
As written it would have logged "Installer not staged" on every 15-minute cycle
and never installed anything, with no error anywhere. Exactly the silent-failure
shape this whole day has been about, and it would have looked like the API being
unreachable.
Resolves via a -KioskRoot parameter defaulting to kiosk\ next to the script, so
the bootstrap and its payload travel together regardless of where the tree is
staged. display-type.txt still comes from EnrollmentRoot, which is correct - that
one really is at C:\Enrollment.
A PXE-imaged display ends up with no GE-Enforce client at all. Confirmed on
579C144, 2026-08-06:
is the client installed? NOT FOUND
scheduled tasks that would run it? NONE
Not a broken configuration - nothing had ever tried. Install-ShopdbKiosk.ps1
downloads itself from {BaseUrl}/installers/kiosk over HTTPS, and ShopDB is only
reachable after the bay joins the AESFMA wifi SSID, so it cannot run during
imaging. Nothing was arranged to run it afterwards.
09-Setup-Display.ps1 now registers 'ShopDB Kiosk Bootstrap' as a SYSTEM task at
boot and every 15 minutes. Install-ShopdbKiosk-WhenOnline.ps1 does nothing until
ShopDB answers, then runs the vendor installer once, verifies BaseUrl and the
enforce task exist, deletes the staged key and unregisters itself. A bay cabled
up days later still self-configures.
It deliberately does not reimplement the installer - it waits, invokes, verifies
and cleans up, so the vendor script can be replaced wholesale without touching
this.
The key file is LABELLED (collector=, fetch=) rather than positional. The two
tokens are not interchangeable and a mix-up is silent: a fetch token in the
collector slot leaves asset reporting broken while everything looks configured.
A bare unlabelled line is ignored rather than guessed at. Missing keys are not
fatal - the fetch token is unnecessary on an IP-allowlisted subnet, and the
installer skips the asset-report task rather than failing.
Staged on the share: the bootstrap, the vendor installer under kiosk\, and the
updated display setup. The key itself is NOT staged yet - see the commit
discussion; it needs to be labelled with which scope it carries first.
Three defects found by the first real run, on 579C144.
16 GB collection, zip failed. run-enrollment harvests
C:\ProgramData\Microsoft\Provisioning into C:\Logs\PPKG, so the 8 GB
provisioning package existed TWICE under the trees being copied. Compress-Archive
cannot exceed 2 GB and died with "stream was too long". Copies now exclude
*.ppkg, *.wim and *.iso and cap individual files at 100 MB.
Base-URL detection matched the wrong value. The name pattern accepted anything
containing "base", so it picked up baseVersion=2.0.2 from
HKLM:\SOFTWARE\GE\SFLD\Credentials and then probed "2.0.2/api/docs" - making
both UNREACHABLE lines meaningless. Now matches the name exactly and requires the
value to start with http.
Unreachable was reported as a bare failure. ShopDB is only reachable once the bay
has joined the AESFMA wifi SSID, so on the imaging LAN or plain wired an
unreachable result is EXPECTED. It now says so, rather than looking like a fault.
The run still answered the question it was written for: no enforce client is
installed and nothing is scheduled to call the API, so this bay was never going
to reach GE-Enforce regardless of network.
Disabling a startup item through Task Manager or Settings does not remove the Run
key or the Startup shortcut - Windows writes a flag into StartupApproved instead.
So the collector would have listed those entries as present with no indication
they had been switched off, and a bay where someone had already tidied up would
look identical to one where nothing autostarts.
Reads all five StartupApproved keys, including Run32 for the WOW64 view, and
decodes the first byte: 02/06 enabled, 03/07 disabled.
This matters for the current investigation because turning an item off is a
per-machine workaround - the next imaged bay installs and starts it again. The
fix is not installing it, which needs the app-to-autostart mapping this section
preserves.
Every imaging investigation so far has meant walking to a bay and hand-copying
files, and each time discovering another thing we wished we had grabbed at the
same moment. This takes the lot. Read-only; changes nothing.
Collects: computer name (active AND pending, which is how you tell whether the
package's H<serial> -> F<serial> rename has landed or is still queued for the
next reboot), dsregcmd enrollment state, the package self-checks, installed
applications, everything that starts by itself, kiosk shortcut targets, Edge
policy, GE-Enforce config, drivers, disk, network, provisioning sessions, and the
whole of C:\Logs, Panther and the provisioning data plus the diagnostics evtx.
Two things it does deliberately:
Autostart is captured across all four surfaces - Run/RunOnce in BOTH registry
views, all Startup folders, non-Microsoft scheduled tasks, and auto-start
services outside C:\Windows. That combination is what identifies which installer
planted a given autostart entry. A 32-bit installer's Run key lands under
Wow6432Node where 64-bit tooling never looks, which is exactly how the old
Dashboard/Lobby autostart survived an earlier purge.
It TESTS the ShopDB GE-Enforce API rather than just reporting config. Config on
disk proves nothing - a client can be present, configured, and never once
succeed. It reports whether a client exists at all, whether anything is scheduled
to run it, the configured base URL, and then actually probes the endpoint. A 401
is a good result: it proves DNS, routing and TLS work and the service answered.
Only a timeout or DNS failure means unreachable. No token is sent.
Deliberately avoids Win32_Product - querying it triggers an MSI reconfigure of
every installed product, which is slow and can change the machine.
Run it BEFORE lockdown. What it captures is known-CURRENT, not known-good: a bay
straight off the line carries applications that should not be there, because
preinstall.json entries without a PCTypes filter install everywhere. The point is
to have an exact record of what imaging really produces so the unnecessary items
can be identified and filtered. After lockdown you cannot tell whether something
is absent because lockdown removed it or because imaging never installed it.
Staged on the enrollment share alongside the other shopfloor-setup scripts.
PC PURPOSE / TENANT FOR STANDARD AND ENGINEER
The capability was already there - the media carries all six MCL packages
(PERS/SH/LOAN x GCCH/RoW) and Start-BulkEnrollOrchestrator.ps1 picks one from
whatever Select-PCConfig.ps1 returns. The problem was WHEN it asks: a GUI at
first logon, so an otherwise unattended build stops at a dialog until someone
walks over.
Now startnet asks after Standard/Engineer is chosen, while the tech who knows
what the machine is for is standing at it, and writes {Tenant}_{Purpose} to
C:\Enrollment\pc-config.txt. A shim at the path the orchestrator already calls
returns that value and falls through to the vendor dialog when it is absent or
malformed - so "ask at first logon" stays available and bays imaged before this
are unaffected. The vendor script is preserved as Select-PCConfig-vendor.ps1;
replace THAT when the vendor ships a new one.
The tenant is not cosmetic. Insert-BPRTToPPKG.ps1 matches the bulk token on
Tenant AND Purpose, so this selects which token gets injected.
ONLY THE CHOICE IS PRE-SEEDED, deliberately. Injection stays at first logon
because it fetches an encrypted token table from mcl.dwcdn.geaerospace.com,
unreachable from the isolated imaging LAN - which is also why routing MCL
packages through run-enrollment.ps1 would not work: provtool would get a package
whose token is still the Exp_XXXXXXXX placeholder and join nothing.
Standard/engineer previously skipped staging entirely (no ppkg, no PCTYPE), so
the staging gate now also admits PCCONFIG.
3D PARTS KIOSK
The display submenu offered Dashboard and Lobby only. The GE-Enforce display
scope has always had a third entry - 3DPrintRoom -> /shopdb/parts-kiosk - so a
parts kiosk could only be set by editing display-type.txt by hand after imaging.
Added as option 3. The value is a KEY into that map, so the spelling matches
exactly.
Verified: startnet parens balance, every goto resolves, 994 CRLF lines with no
bare LF; shim parses clean. Deployed - boot.wim 4d16c946, shim staged with the
vendor dialog preserved.
OWNERSHIP.md
Every expensive bug in this pipeline has had one shape: two systems setting the
same thing, last writer winning silently. Four happened on 2026-08-06 alone -
computer name (package vs run-enrollment), drive letters (PESetup vs a volume
finder), enrollment (package vs an at-logon -ManualFallback task that syspreped
finished machines), kiosk URLs (GE-Enforce vs site-config). Each is written down
with the evidence so the next person deletes a writer instead of adding one.
share-drift.py
The share is production and the repo is meant to describe it, but drift runs both
ways: live hand-edits nobody committed, and repo fixes never deployed. The
unattend outage lived only on the share while the repo copy was fine, and nothing
compared them.
Each mapped pair is classified. git-owned means the repo wins and the pair must
match - those fail the run. unreconciled means the two have genuinely diverged
and nobody has decided; reported, not failed. The unattends are unreconciled on
purpose: live is ~17 KB against ~12 KB in the repo, so a blind push would regress
production. Reads over SSH via base64 so BOM and CRLF survive the hop.
First run: 8 git-owned pairs all match, 4 known-unreconciled.
Run-ShopfloorSetup.ps1
Corrects a comment that was actively misleading. It claimed shopfloor PCs are
"vanilla by design" and that the orchestrator runs -ManualFallback to skip BPRT
injection and the package entirely. Shopfloor bays DO enrol - the SFLD package
joins Entra with its BPRT token and a human assigns the device category in
Intune. -ManualFallback runs sysprep /oobe /reboot, which is why wiring it to an
at-logon task destroyed the deployment chain.
The absent Entra wait is still correct, for a different reason: at that point the
bay is on the isolated PXE LAN with no route to Entra (579C144 held 172.16.9.81
and 172.24.19.142, neither in the production ranges). sync_intune retries until
the tech re-cables. "Entra ID Joined: false" right after imaging is normal.
Three changes aimed at the same failure mode: a bay that images green and is
silently unusable, diagnosed only by walking over and copying files off by hand.
VERIFY STAGING (startnet, at :pctype_done)
Checks pc-type.txt, Run-ShopfloorSetup.ps1, shopfloor-setup/common and
shopfloor-setup/<PCTYPE> exist on the applied volume before anything depends on
them, retries the small trees once, and prints a loud banner if the retry does
not fix it. The enrollment package is checked but never blindly re-pulled - it is
8 GB, so a miss is reported instead.
Done here because a tech is still at the machine: a short copy found in WinPE
costs 30 seconds, the same copy found at first logon costs 20 minutes, and found
never costs a rebuild. Four Display bays sat green at stage 2 for weeks.
HARVEST LOGS (startnet, after PESetup exits)
Collects X:\*.log, the generated X:\Unattend.xml, PESetup's own log from the
target's Panther directory and winpe-staging.log into
\<server>\enrollment\imaging-logs\<serial>\, plus a build-context.txt naming
PCTYPE, PPKG, machine number and media. All of it was being discarded at reboot.
Runs after PESetup exits so the logs are final, which means re-mapping Y: since
cleanup already dropped it. Best-effort throughout - a bay must never fail to
reboot because a log copy failed.
W: WAIT CAP 20 -> 45 MINUTES
270 polls instead of 120. Fine on NVMe either way, but a WIM apply can exceed 20
minutes on slow media, and the failure mode is the os_not_found banner plus
nothing staged. The loop still exits the moment the SYSTEM hive appears.
RUN-ENROLLMENT RUNS ONCE
Marker at C:\Enrollment\.ppkg-applied, written on exit 0 and also on 0x800700B7
ERROR_ALREADY_EXISTS. Observed running twice on 579C144; the second pass
re-applied a pending rename over the package's own and otherwise did nothing.
Verified: startnet parens balance, every goto resolves, 899 CRLF lines with no
bare LF; run-enrollment parses clean under the PowerShell parser. Deployed -
boot.wim md5 159c2a4d, live run-enrollment dce9d50a.
The package declares <DNSComputerName>F%SERIAL%</DNSComputerName>, so bays are
meant to come up as F<serial>. This script hardcoded a rename to E<serial>,
which is a second pending rename racing the package's own.
Observed on 579C144, 2026-08-06:
13:31:55 run-enrollment Rename-Computer -> E579C144 (pending)
13:32:17 ppkg Reboot/DeviceName -> F%SERIAL% (pending)
Provisioning-Diagnostics: "Reboot successfully scheduled.
Device name set successfully."
13:36:45 run-enrollment runs AGAIN -> E579C144 (overwrites F)
~13:37 reboot; E579C144 wins
Last writer wins at reboot, so the hardcoded prefix silently beat the package.
The bay came up E579C144 with the ppkg reporting no errors at all - 535
Provisioning-Diagnostics records, every one of them Level 4.
Nothing in this repo ever produced an F name; grep for a prefix rule or a
namePrefix setting finds nothing. The convention only works if naming is left to
the package, so the rename is removed rather than corrected to F. That also
makes it site-agnostic: a different package can name bays differently without a
script edit.
The double execution is a separate defect and is left alone for now. With the
rename gone a second run is harmless: provtool returns 0x800700B7
ERROR_ALREADY_EXISTS and changes nothing.
The dashboard moved to /shopdb/shopfloor. Every reference here still pointed at
/shopdb/shopfloor-dashboard/, which no longer resolves.
Display-Lobby was also pointing at the DASHBOARD url, not the lobby one, so lobby
screens showed the shop floor dashboard. The routes now follow the authoritative
map in shopdb-flask plugins/geenforce/seed_display_scope.py:
Dashboard -> /shopdb/shopfloor
Lobby -> /shopdb/tv
3DPrintRoom -> /shopdb/parts-kiosk (no imaging-time menu option yet)
Changed: urls.shopfloorDashboard, Display-Dashboard.edgeHomepage,
Display-Lobby.edgeHomepage, and the Shopfloor Dashboard fallback in
08-EdgeDefaultBrowser.ps1.
Note these values are now a backstop rather than the primary path. Displays are a
single image whose variant comes from C:\Enrollment\display-type.txt, and the
GE-Enforce display scope delivered over the shopdb API writes the kiosk Startup
shortcut itself - preferring the server-side role from Settings > Dashboard
Defaults and falling back to display-type.txt. That dispatcher also SWEEPS
shortcuts matching shopfloor-dashboard, so a stale value here would have been
deleted on the next enforce cycle rather than honoured.
Edited by line position, not string match: the two edgeHomepage values were
textually identical and belong to different display blocks. Formatting preserved
- a json.dumps round-trip reflowed 632 lines and was reverted.
An OptiPlex Micro 7020 reported "no update in catalog" at the WinPE menu. The
firmware was staged all along - OptiPlex_7020_1.22.1_SEMB.exe, 102 MB, sitting on
the share - but models.txt listed only "Tower Plus 7020". check-bios.cmd matches
with find /I, a substring test against the WMI model name, and "Tower Plus 7020"
is not a substring of "OptiPlex Micro 7020", so every Micro, SFF and Tower 7020
silently skipped its firmware update.
The 7010 family three lines above already shows the intended shape: one SEMB
package behind Micro, SFF Plus and Tower Plus entries. Dell packages 7020 the
same way, covering Micro, Micro Plus, SFF, SFF Plus, Tower and Tower Plus, so all
six now point at the one package and the stray Tower Plus line moves up to join
its family.
Verified by simulating the matcher over the manifest: all six variants resolve to
the 7020 package, no duplicate tokens, and 7010 still resolves to its own. Note
that "Small Form Factor 7020" spelled out does NOT match - the short form is
correct, since HardwareDriver.json's modelswminame field uses SFF 7020.
Deployed to the live share, where check-bios.cmd reads it directly off B:, so no
boot.wim rebuild is involved. Previous manifest kept at
/home/pxe/models-rollback-20260806.txt.
Join-Path routes a drive-qualified path through the PowerShell provider, so
it returns null when that drive has gone away mid-cycle, and every consumer
then bound the null straight into Test-Path -LiteralPath and crashed the
entry with a message that names neither the entry nor the path.
Replace the six Join-Path $InstallerRoot sites with Join-InstallerPath, which
does plain string math, and have all six callers treat a null resolve as
"not found" and log it. Only the PS1 branch had a guard before, and it
covered a null Script value, not a null resolved path.
The helper deliberately avoids [IO.Path]::Combine: its separator and
rooted-path rules follow the host platform, so behaviour cannot be verified
off-Windows. It also rejects rooted values ("\x", "D:\x", "\\server\share"),
which Combine would have resolved outside the share root. Checked all 36
path values across the share manifests: none are rooted today.
Behaviour verified against the real manifest values (forward slashes,
backslashes, trailing-slash root, null, whitespace, rooted, UNC).
GE-Enforce mounts the SFLD share on W: and holds it for the whole enforce
cycle, passing W:\<pctype> as -InstallerRoot to Install-FromManifest. Two
scripts that run inside that cycle, as SYSTEM in the same drive namespace,
mapped W: for their own use and deleted it on exit:
Restore-UDCData.ps1 manifest PS1 entry, mounts the UDC backup share
Update-MachineNumber.ps1 "Apply Machine Number" task, same backup share
Once W: is gone, the next manifest entry's Join-Path resolves against a dead
drive qualifier. In Windows PowerShell 5.1 that emits nothing rather than
throwing, so the null lands in Test-Path -LiteralPath and the entry dies with
"Cannot bind argument to parameter 'LiteralPath' because it is null".
Observed on a collections bay: the controller-credential entry, which runs
immediately after UDC Data Restore, failed this way while the entry one line
earlier had resolved a W: path successfully one second before.
Restore-UDCData now uses R: and Update-MachineNumber uses N: for the UDC
backup share. Neither letter is referenced anywhere else in the tree; V: was
not an option because Restore-EDncReg and Update-MachineNumber's NTLARS
restore already use it.
The re-mount at GE-Enforce.ps1:239-244 does not cover this. It runs after the
manifest loop and blames idle SMB timeout, but the whole cycle took one
second, so timeout was never the cause.
Replace the hardcoded GEA Shopfloor PC-type sub-menu with a data-driven one:
- menu.json on the enrollment share lists the shopfloor items {key=PCTYPE, label, hint, enabled}; key must match a shopfloor-setup/gea-shopfloor-* handler dir.
- select-shopfloor-type.ps1 renders it in WinPE and writes the chosen PCTYPE (mirrors the CMM bay picker); startnet.cmd runs it and falls back to the baked-in menu if the share/picker is unavailable.
- Webapp /shopfloor-menu editor: reorder/rename/hide/add items; the PC-type is a dropdown of existing handler dirs (can't wire a choice to a non-existent type); writes menu.json. Nav link under Tools.
Kills the duplicated-knowledge problem (menu list was hardcoded in startnet AND the handler dirs AND site-config); add a PC-type = drop in the handler dir + it appears in the menu.
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>
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>
- 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>
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>
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>
- 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>
- 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>
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>
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>
- 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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>