geenforce: shadow runs inside the enforce cycle, not on its own clock

Test bays reported 0 installed / 0 skipped / 0 failed and nothing was being
processed. The share is a TRANSIENT mount: GE-Enforce.ps1 maps it for the length
of its cycle and unmounts at the end - mounted 12:10:02, unmounted 12:10:27 on
FB9TP7V3. The shadow task ran on its own 15-minute schedule, so it woke with the
drive gone, handed the engine a path that no longer resolved, and the summary
zero-filled. A silent nothing, indistinguishable from a healthy no-op, which is
why it read as "not processing" rather than as a failure.

Register-ShopdbShadow.ps1 is replaced by Invoke-ShopdbShadow.ps1, which runs AS
a manifest entry and invokes the runner directly. The share is mounted because
the enforce cycle is what called us; there is one cadence instead of two; and
there is no task to register, drift, or heal. It also unregisters the superseded
task, so bays that already carry it clean themselves up rather than keeping a
run that cannot see the share and overwrites the real result with 0/0/0.

The runner gains the Test-Path guard that should have been there: an unreachable
-ShareManifestPath now logs, writes an event, and reports a real failure naming
the reason, instead of running the engine against nothing. That silent zero is
what made this expensive to find.

Verified on the win11 VM with a substituted drive removed between runs - the
same call gives real counts while mapped and the guard when not. The earlier
task self-heal is moot now; it was a fix at the wrong layer, and testing against
a local path is what hid a mount I had already noted was dynamic.
This commit is contained in:
cproudlock
2026-08-13 14:52:56 -04:00
parent dfbc12a172
commit 1078ac03df
3 changed files with 108 additions and 129 deletions

View File

@@ -131,6 +131,29 @@ try {
if ($ShadowMode -and $ShareManifestPath) {
# Shadow: install from the share exactly as today (no payload resolve,
# no common merge - the share already carries its own common scope).
#
# GUARDED, because the share is a TRANSIENT mount. GE-Enforce.ps1 maps it
# for the length of its cycle and unmounts at the end, so a caller that
# runs on its own schedule finds the drive gone. Handing the engine a
# path on a dead drive returned nothing usable and the summary zero-filled
# to 0 installed / 0 skipped / 0 failed - a silent nothing that reads
# exactly like a healthy no-op. Shadow must run INSIDE the enforce cycle;
# this makes the alternative loud instead of invisible.
if (-not (Test-Path -LiteralPath $ShareManifestPath)) {
$reason = "share manifest not reachable at $ShareManifestPath " +
'(is the share still mounted? shadow must run inside the ' +
'enforce cycle)'
Write-Log $reason 'WARN'
Write-ShopdbEventLog -Message ("GE-Enforce shadow could not reach the share manifest for scope '$Scope': $reason") -EntryType 'Error' -EventId 1002
try {
$failReport = New-ShopdbReport -Scope $Scope -AppliedVersion 0 -Summary @{
Installed = 0; Skipped = 0; Failed = 1; Filtered = 0; EnforcerVersion = '2.6'
Results = @(@{ Name = '(share-manifest)'; Action = 'failed'; Message = $reason })
}
Send-ShopdbReport -Config $config -Report $failReport | Out-Null
} catch {}
exit 0
}
$manifestToRun = $ShareManifestPath
} else {
# Optional common-scope inheritance (OFF by default; displays are