Commit Graph

272 Commits

Author SHA1 Message Date
Oliver Kieselbach feff123f11 feat(backend,agent,web): hosted diagnostics destination + cascade-delete cleanup
Adds an opt-in second destination for agent diagnostics uploads:
- CustomerSas (default, unchanged): tenant's own SAS-backed container
- Hosted (new, opt-in via explicit admin click): backend's storage account
  under {tenantId}/AgentDiagnostics-...zip, with per-upload 15-min
  blob-scoped Write+Create SAS minted via the Function App's MI

Closes the long-standing gap where Session.DiagnosticsBlobName was captured
into the deletion-manifest but the blob was never actually cleaned up by
the cascade. Hosted blobs are always deleted (we own the storage); customer
blobs are deleted only when the SAS includes the 'd' permission, otherwise
logged + skipped.

Existing tenants change nothing: the new TenantConfiguration.DiagnosticsUploadDestination
field defaults to CustomerSas, no silent flip. Hosted requires an explicit
admin click + Save with a clearly marked "data leaves your tenant" disclosure.

Tests: Backend 1844, V2 1076, V1 538, vitest 380 (+78 new across all suites).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 18:45:01 +02:00
Oliver Kieselbach e64b795b28 fix certificate selection 2026-05-18 12:25:45 +02:00
Oliver Kieselbach c0a8f2ed27 fix(agent/v1/sw-inventory): mirror V2 fix — accept AAD/MSA SIDs and emit real registry path
V1 carried the same two bugs as V2: the per-user profile enumeration filter
only matched S-1-5-21-* SIDs, silently skipping the AAD/MSA cloud SIDs
(S-1-12-1-*) that Win10 1903+ uses for AAD-joined users — so on Win11 AAD
boxes the per-user Uninstall hive of the actually-logged-in user was never
read. Additionally, CollectFromKey logged "{source}\{subKeyPath}" which
mashed the telemetry tag (HKU_<RID>) with the relative path, producing a
non-path string like "HKU_1000\S-1-5-21-...\SOFTWARE\..." in debug logs.

Same patch as V2 commit 6c4f7250: extend SID filter to include S-1-12-1-*,
log rootKey.Name (HKEY_USERS / HKEY_LOCAL_MACHINE / ...) as the path prefix
and report the telemetry tag separately as "(source=...)".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 22:53:33 +02:00
Oliver Kieselbach b4e98e32eb fix(agent): add MS-Organization-Access cert fallback to TenantIdResolver
Adds EntraDeviceCertHelper that reads the AAD-Join device certificate's
TenantId extension (OID 1.2.840.113556.1.5.284.5) from LocalMachine\My.
Wired as the 5th and final synchronous probe in TenantIdResolver, after
the four registry probes:

  1. enrollments_registry         (Type=6, Intune MDM)
  2. enrollments_registry_fallback (any Type, AADTenantID present)
  3. cloud_domain_join_tenant_info
  4. cloud_domain_join_join_info
  5. ms_organization_access_cert  <- new

Targets the Autopilot pre-provisioning / hybrid window where the cert
can land before the CloudDomainJoin registry writes complete. Lets the
agent boot without falling through to the registry-watcher awaiter and
burning the 600s wait window.

Helper hardening over the original draft:
- Filters to currently-valid certs (NotBefore <= now <= NotAfter) and
  picks latest NotAfter when multiple match, defending against an
  expired sibling from a prior re-enrollment.
- If the highest-priority cert lacks the OID extension, walks through
  the remaining candidates instead of bailing on the first miss.
- Logger emits Info on hit (thumbprint + NotAfter) and Warning on miss
  with the specific reason (no cert / extension missing / parse failed).

Mirrored across V1 (Agent.Core) and V2 (Agent.V2.Core).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 22:19:02 +02:00
Oliver Kieselbach b67d68c552 fix(agent): TenantIdResolver fallback for non-Type-6 enrollments + UPN redaction
Two-pass scan of HKLM\SOFTWARE\Microsoft\Enrollments: pass 1 keeps the
existing Type=6 (Intune MDM) preference, pass 2 falls back to any
non-Type-6 sub-key carrying a non-empty AADTenantID. Targets the
Autopilot pre-provisioning / hybrid window where Type=6 sits at
EnrollmentState=1 with AADTenantID still missing while a Type=26
AAD-Join entry already carries the TenantID. Observed at customer
where the agent burned ~30min waiting for CloudDomainJoin\TenantInfo
instead of resolving immediately from Type=26.

Captures the enrollment UPN and classifies it. Known Autopilot
pre-provisioning placeholders (fooUser@... Intune-MDM leg,
autopilot@... Entra-Join leg, both generated by the Windows
MakeFakeUserEmail code path) are logged in full with annotation;
anything else is treated as a real user UPN and redacted to
<redacted_real_user_upn> since UPNs are PII.

Mirrored across V1 (Agent.Core) and V2 (Agent.V2.Core).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 17:13:48 +02:00
Oliver Kieselbach 4a996070b8 feat(noise): add ModernDeployment EventID 1010 to harmless default list
EventID 1010 (Autopilot.dll WIL hardwareinfo.cpp HRESULT 0x80070002 /
file-not-found probe) is a known noise event with no enrollment impact.
Downgraded to Debug across V1+V2 trackers, shared default list, admin
config fallback, and web UI defaults. ConfigVersion bumped 26 -> 27.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 22:54:30 +02:00
Oliver Kieselbach 58be7d19c3 fix(agent): force Registry64 view + trim TenantIdResolver miss log
Registry reads now go through RegistryKey.OpenBaseKey(.., Registry64)
explicitly. SDK-style net48 EXEs can resolve AnyCPU to 32-bit at runtime
depending on MSBuild defaults; in that case
HKLM\SOFTWARE\Microsoft\Enrollments redirects to the WOW6432Node mirror,
a different (and usually stale/empty) hive that never carries the active
MDM enrollment. Forcing Registry64 makes the resolver bitness-independent
regardless of how AnyCPU resolves.

Diagnostic-on-miss is now compact:
* Sub-keys without an EnrollmentType value (Context, Ownership, Status,
  ValidNodePaths) are recognized as structural and skipped silently.
* The detail list contains only Type=6 (Intune MDM) entries with their
  EnrollmentState and AADTenantID presence — that's the only enrollment
  flavor that ever carries the TenantId.
* Non-Type-6 enrollments collapse into a single counter so a heavily
  enrolled device (e.g. dev box with 30+ historical enrollments) no
  longer floods the agent log with one Warning per sub-key.

Mirrored across the V1 + V2 resolver shipped in 99ec2517 / fc950757.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 20:38:01 +02:00
Oliver Kieselbach 99ec25175d feat(agent): backport V1 TenantIdResolver with CloudDomainJoin fallback
Mirrors the V2 resolver shipped in fc950757 to V1 so the legacy rail
gets the same OOBE-race protection. Probe order: Enrollments registry
→ CloudDomainJoin\TenantInfo → CloudDomainJoin\JoinInfo, source-tagged
log line on hit, full diagnostic block on miss (which roots exist,
which enrollment GUIDs were seen with their EnrollmentType /
EnrollmentState, which JoinInfo thumbprints).

Removes the inline GetTenantIdFromRegistry helper from V1 Program.cs
and routes all three callsites (LoadConfiguration, await-enrollment
re-resolve, pre-flight bail) through the new resolver. The bail site
re-probes with a logger when the await branch was skipped, so the
diagnostic block lands in the agent log on a real miss instead of
just the generic "registry empty" error.

Updates a stale doc comment in V2 Program.GatherRulesMode.cs that
referenced the removed V1 method name.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 19:13:59 +02:00
Oliver Kieselbach 9f3ee5eedb fix(agent): V1 bails cleanly when TenantId missing instead of crashing
Customer gardnermedia (2026-04-30) hit a fatal V1 startup crash:
"InvalidOperationException: Invalid agent configuration" thrown from
MonitoringService..ctor. Root cause: LoadConfiguration returned a config
with TenantId=null because HKLM\SOFTWARE\Microsoft\Enrollments did not
yet carry an EnrollmentType=6\AADTenantID entry and no bootstrap-config
.json was present. Without a guard, control reached MonitoringService
construction and IsValid() threw an unhandled exception → crash log →
"exception_crash" classification on the next start.

V2 already bails cleanly in this case (AgentBootstrap.Run line 123-127).
This commit gives V1 the same safety net plus a more diagnostic message
so any future occurrence is immediately analyzable from the crash log.

Changes:
* Program.cs: pre-flight TenantId guard right after the
  --await-enrollment block. Logs an Error and returns cleanly so the
  ProcessExit handler writes clean-exit.marker — no spurious crash log,
  Scheduled Task simply retries on the next trigger.
* MonitoringService..ctor: replace the opaque "Invalid agent
  configuration" throw with one that names the missing fields
  (ApiBaseUrl / SessionId / TenantId).
* MonitoringServiceConfigValidationTests: 5 new xUnit cases covering
  each missing-field path plus the multi-missing case.
* V1 buildcounter bump.

V2 unchanged — its existing AgentBootstrap guard already covers this
class of crash.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 15:59:29 +02:00
Oliver Kieselbach e88c825b56 feat(agent,rules): VM detection on hardware_spec + ANALYZE-SEC-001 skip on VMs
Adds a coarse virtual-machine flag to every hardware_spec event so the rule
engine's preconditions feature can filter device-specific rules. Wires the
new flag into ANALYZE-SEC-001 (Secure Boot UEFI CA 2023): the rule no longer
fires on Hyper-V / VMware / VirtualBox / Xen / QEMU / Parallels / KVM
guests, where the firmware DB is host-controlled and the inside-the-VM
UEFICA2023Status registry value is not actionable.

Agent (V1 + V2)
- Centralized VM detection in DeviceInfoProvider.IsVirtualMachine(mfr, model)
  alongside the existing GetManufacturer / GetModel helpers (which already
  handle Lenovo's Win32_ComputerSystemProduct.Version quirk). Reused, not
  duplicated — same source of truth as SessionRegistration / DiagnosticsPackage
  / GatherRulesMode.
- New CollectSystemInfo step in DeviceInfoCollector.HardwareSpec emits
  systemManufacturer, systemModel, isVirtualMachine onto the hardware_spec
  event. No new WMI query — calls into DeviceInfoProvider.
- Conservative bias: Microsoft Corporation manufacturer is shared between
  Hyper-V VMs and physical Surface devices, disambiguated via model
  ("Virtual Machine" → VM, "Surface Laptop 5" → physical).

Rule
- ANALYZE-SEC-001 gains a precondition (equals isVirtualMachine=false) and
  bumps to v1.1.0 so EnsureBuiltInRulesSeededAsync re-saves it for tenants
  on next backend cold start.
- Old agents (no isVirtualMachine field on hardware_spec) → fail-closed →
  rule is silently skipped on those devices until the agent updates. Safe
  default for a security rule.

Tests
- DeviceInfoProviderVmDetectionTests (V1 + V2 mirror, 18 cases each):
  Hyper-V / Cloud PC / VMware / VirtualBox / Xen / QEMU / Parallels / KVM
  positive + Surface Laptop/Pro/Book + Lenovo / Dell / HP negative + null/empty
  edge cases. Pinned at the central DeviceInfoProvider so all consumers benefit.
- 781/781 backend, 525/525 V1 agent, 800/800 V2 agent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 23:35:00 +02:00
Oliver Kieselbach 830a27bb70 refactor(decisioncore): add DecisionCore shared library scaffolding (M1)
New netstandard2.0 project src/Shared/AutopilotMonitor.DecisionCore/ with
14 scaffolding types across Signals/, State/, Classifiers/, Engine/.
Agent.Core and Functions reference the library without call-sites; the
full solution builds 0 errors, 0 warnings. DecisionEngine.Reduce is an
intentional NotImplementedException stub — M3 fills in the partial-class
reducer.

Invariants wired into the scaffolding:
 - Immutable DTOs (sealed + get-only + With-methods) per L.3
 - Pflicht-Evidence validation in Evidence ctor per L.5
 - KindSchemaVersion enforcement per L.6
 - AssemblyVersion 2.0.0.0 as ReducerVersion source per §2.10

Architecture principles L.1–L.18 extracted to
.claude/architecture-principles.md as persistent Claude-session context.

Plan: plans/REFACTOR_AGENT_V2.md §4 M1. Zero runtime risk.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 13:29:19 +02:00
Oliver Kieselbach 6f758ea0f1 late-aadj fix 2026-04-17 14:47:16 +02:00
Oliver Kieselbach 6719464f34 fix WhiteGloveClassifier and Completion logic, fix Phase Timeline 2026-04-17 12:59:38 +02:00
Oliver Kieselbach 6efae2c709 feat(agent): WhiteGlove Detection Classifier and late-AADJ support 2026-04-17 12:35:25 +02:00
Oliver Kieselbach 64240249ac dashboard performance and more coding safety checks in integrity analyser 2026-04-17 11:16:41 +02:00
Oliver Kieselbach 9d2d6c3ae7 run integrity analyzer only on startup 2026-04-17 00:29:19 +02:00
Oliver Kieselbach 2eb6d87b4d feat(agent): added IntegrityBypassAnalyzer 2026-04-17 00:20:24 +02:00
Oliver Kieselbach 8d661bbd70 whiteglove event 509 wording 2026-04-16 23:16:54 +02:00
Oliver Kieselbach fd73f5abfb prevent doubel completion_decision_process events, fix phase timeline 2026-04-16 22:31:35 +02:00
Oliver Kieselbach 4b3db3a1cd whiteglove completion logic and orphan events handling 2026-04-16 20:06:28 +02:00
Oliver Kieselbach 7b8a242d44 fix HybridJoin detection 2026-04-16 14:37:22 +02:00
Oliver Kieselbach bb320e0ec8 added additional foouser detection (soft WhiteGlove Indicator) 2026-04-16 14:30:35 +02:00
Oliver Kieselbach aba8df6572 agent StateMachine fixes 2026-04-16 14:15:40 +02:00
Oliver Kieselbach 3cb3df2b16 completion logix fix 2026-04-16 12:52:38 +02:00
Oliver Kieselbach 9fd2cba424 fix(agent): prevent WhiteGlove sessions from being misclassified as self-deploying
WhiteGlove (Pre-Provisioning) sessions were incorrectly classified as
self-deploying because OnDeviceSetupProvisioningComplete and the esp_exiting
handler both race ahead of the signal-correlated WhiteGlove detector (30s
timer + 10min stability window). Two WhiteGlove signals existed but were
never consumed by completion logic:

1. EventID 509 (whiteglove_started) — already detected by
   ModernDeploymentTracker but not exposed to EnrollmentTracker
2. SaveWhiteGloveSuccessResult=succeeded in DeviceSetup registry JSON —
   present in esp_provisioning_raw but invisible to ParseSubcategories
   (no *Subcategory suffix)

Adds WhiteGlove guards at both device-only completion paths:
- OnDeviceSetupProvisioningComplete (registry polling path)
- OnFinalizingSetupPhaseTriggered esp_exiting + SkipUserStatusPage path

Exposes IsWhiteGloveStartDetected and HasSaveWhiteGloveSuccessResult
through EspAndHelloTracker. Mirrors guards in CompletionStateMachine.
Backend safety net: classifies whiteglove_started events and sets
IsPreProvisioned=true early as fallback for older agents.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 10:51:22 +02:00
Oliver Kieselbach 9447e3c9d8 refactor(agent): move StallProbeCollector + drop empty legacy folders
Part 5 of 5 in Monitoring/ bounded-context restructure. StallProbeCollector
lands in Enrollment/SystemSignals/. Empty Core/Tracking/Collectors/Network/
Analyzers/Flows/Replay/ directories removed. No behavior change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 01:49:24 +02:00
Oliver Kieselbach 6d2b06d004 refactor(agent): move enrollment domain into Enrollment/ feature subfolders
Part 4 of 5 in Monitoring/ bounded-context restructure. Feature-based split:
Enrollment/ (orchestrator+persistence), Flows/, Ime/ (log parsing, parser,
positions, app/script state, replay, process watcher), SystemSignals/
(ESP/Hello/ShellCore/ModernDeployment/Provisioning/DesktopArrival), and
Completion/ (state machine, guards, handler). Pure folder move +
namespace update; no behavior change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 01:44:46 +02:00
Oliver Kieselbach 9f053279f5 refactor(agent): move telemetry collectors/analyzers/gather into Telemetry/
Part 3 of 5 in Monitoring/ bounded-context restructure. Split by purpose
(periodic metrics, hardcoded analyzers, backend-driven gather rules,
device info). Pure folder move + namespace update; no behavior change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 01:38:47 +02:00
Oliver Kieselbach 317677abf6 refactor(agent): move Network/ + Core upload internals to Transport/
Part 2 of 5 in Monitoring/ bounded-context restructure. Network/* plus
Core/EventSpool + Core/EventUploadOrchestrator moved into Monitoring/Transport/.
Pure folder move + namespace update; no behavior change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 01:33:48 +02:00
Oliver Kieselbach e1117d7db8 refactor(agent): move Core orchestrators to Runtime/, flatten Interop/
Part 1 of 5 in Monitoring/ bounded-context restructure. Moves Monitoring/Core/*
(minus EventSpool/EventUploadOrchestrator/DeviceInfoProvider/EnrollmentCompletionHandler
which belong to later PRs) into Monitoring/Runtime/. Interop/ stays on
Monitoring root. Pure folder move + namespace update; no behavior change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 01:30:05 +02:00
Oliver Kieselbach c460a9f528 optimizations, DO timeout and PerfCollector WMI to SysCall 2026-04-16 00:36:05 +02:00
Oliver Kieselbach 4d40cdf3c0 fix completion state machine (esp state wasn't tracked all the time) 2026-04-15 14:52:53 +02:00
Oliver Kieselbach a3d37c67ac added more http compression (responses also) 2026-04-15 00:34:18 +02:00
Oliver Kieselbach 01e29da0f2 feat: Server SessionActions and action signal consolidation on agent side 2026-04-14 23:42:10 +02:00
Oliver Kieselbach 95cb8a3a7e feat: define harmless event ids for collectors 2026-04-14 22:56:16 +02:00
Oliver Kieselbach 45edbab0e1 feat: enhanced analyze rule engine (in not_in support) MarkSessionAsFailed support, new ESP rule for cert errors 2026-04-14 21:46:20 +02:00
Oliver Kieselbach 52fcfce271 feat: allow agent downgrade 2026-04-14 17:51:50 +02:00
Oliver Kieselbach 6702e1d43b feat: added DevPrepValidator (device association) and restructured agent to distinguish Classic vs v2 autopilot flow, in addition option for dev build added 2026-04-14 16:09:32 +02:00
Oliver Kieselbach d391900d6d added stateMachine discrepancy check events for further validation of stateMachine and upcoming final switch 2026-04-14 14:55:05 +02:00
Oliver Kieselbach aa747d4a35 extracted ShellCoreTracker + ProvisioningStatusTracker from EspAndHelloTracker
Completes the composition split: EspAndHelloTracker is now a thin coordinator
(~290 LOC) that forwards events between four focused sub-trackers:

  - HelloTracker              (820 LOC, previously extracted)
  - ModernDeploymentTracker   (480 LOC, previously extracted)
  - ShellCoreTracker          (325 LOC, NEW)
  - ProvisioningStatusTracker (650 LOC, NEW)

ShellCoreTracker owns Shell-Core 62404/62407 handling, ESP-exit + WhiteGlove
fire-once guards, failure classification + backfill. Cross-notifies HelloTracker
on Hello wizard start and ESP exit. Core processing extracted to ProcessEvent
with primitive inputs so tests drive it without EventLogWatcher.

ProvisioningStatusTracker owns registry polling (RegNotifyChangeKeyValue),
category/subcategory parsing for both flat + nested JSON formats, subcategory
transition detection with noise filtering, categorySucceeded resolution,
DeviceSetup fallback timer (5s recheck after 30s for Win 25H2/26200 builds
that never set the boolean), and the public snapshot APIs.

Coordinator's public API (events, properties, methods) preserved byte-for-byte
for CollectorCoordinator + EnrollmentTracker — no integration changes required.

Adds 50 xUnit tests:
  - ShellCoreTrackerTests (20): Hello wizard start notification, WhiteGlove
    fire-once, all 6 failure keywords, ESP exit flow, WhiteGlove-vs-Exiting
    regex precedence, backfill dedup semantics.
  - ProvisioningStatusTrackerTests (30): first-seen / resolved / failure paths,
    flat + nested subcategory formats, state inference, snapshot APIs,
    noise-filtering of notStarted→in_progress transitions, progression to
    succeeded as a transition event, JSON helper edge cases.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:21:45 +02:00
Oliver Kieselbach a8f6325ea9 extracted HelloTracker from EspAndHelloTracker
Moves WHfB policy detection, UDR event handling (300/301/358/360/362/376),
HelloForBusiness watcher (3024/6045), Hello wait/completion timers and
HelloOutcome state machine into a dedicated HelloTracker class (820 LOC).

Cross-boundary with ESP (Shell-Core 62404/62407) is narrowed to two
explicit notify methods: NotifyHelloWizardStarted + NotifyEspExited.

Public API of EspAndHelloTracker is unchanged — IsPolicyConfigured,
IsHelloCompleted, HelloOutcome, StartHelloWaitTimer, ForceMarkHelloCompleted,
ResetForEspResumption and the HelloCompleted event now delegate to the
composed HelloTracker. CollectorCoordinator + EnrollmentTracker integration
requires no changes.

Adds 29 xUnit tests covering: UDR terminal events, 6045 skip HRESULT,
wait/completion timer paths (policy-enabled extended wait, policy-disabled
immediate completion, wizard-started vs wizard-not-started outcomes),
cross-boundary notifications, ForceMarkHelloCompleted semantics and
ResetForEspResumption state reset.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:05:15 +02:00
Oliver Kieselbach 97fb112c00 extracted ModernDeploymentTracker 2026-04-14 12:54:52 +02:00
Oliver Kieselbach 665a9a14cc better agent self-update observability 2026-04-14 09:33:44 +02:00
Oliver Kieselbach fa1128212f added agent core tests 2026-04-14 01:39:48 +02:00
Oliver Kieselbach bb3efae5dc refactor CollectorCoordinator (method logic extraction) 2026-04-14 01:00:24 +02:00
Oliver Kieselbach 918d6c0ee7 Refactor MonitoringService for better reliability and testability 2026-04-13 23:51:24 +02:00
Oliver Kieselbach f2bbee6aa7 agent feat: added CompletionStateMachine enhance reliability and testability 2026-04-13 22:14:44 +02:00
Claude 4d73c86641 feat: add event backfill for targeted ModernDeployment events (Event 509)
Solves the timing gap where WhiteGlove Event 509 fires during OOBE
before the agent starts (pre MDM-enroll). On startup the agent now
scans the ManagementService event log for targeted events within a
configurable lookback window (default 30 min) and emits them with
a backfilled=true marker for trace analysis.

Key changes:
- Extract ProcessModernDeploymentRecord from live handler for reuse
  by both EventLogWatcher callback and EventLogReader backfill
- Add BackfillTargetedModernDeploymentEvents() with persisted state
  (whiteglove-backfill.json) so subsequent agent restarts skip the
  scan and avoid duplicate emissions
- Add config fields ModernDeploymentBackfillEnabled and
  ModernDeploymentBackfillLookbackMinutes to CollectorConfiguration
- Wire up stateDirectory and config in MonitoringService
- All emitted events now carry Data["backfilled"] = true/false

https://claude.ai/code/session_01ToApHGezTGcSkA6NVbVZkG
2026-04-12 10:11:19 +00:00
Oliver Kieselbach 1a5ab674b0 more whitglove detection logic (event 509) 2026-04-11 00:41:45 +02:00
Oliver Kieselbach d55775999d probe collector enhancements 2026-04-10 17:29:04 +02:00