mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-25 05:00:07 +00:00
d02fa454a7
Misclassification audit 2026-07-16 (1000 non-succeeded sessions, 22 deep
dives) surfaced six systematic misdeclaration sources. This implements
all of them:
1. DecisionCore: HelloNeverObservedPromote in the esp-exit advisory
handler — when the Hello policy read never committed and no wizard
was ever seen, hello_resolution is structurally unresolvable; promote
to AwaitingHello + hello_safety instead of hard-failing a provably
finished enrollment (sessions 2dac8298/7635ab18). Placed after the
enforcement-progress re-arm so active installs still win; advisory-
anchor variant stays exempt.
2. Ingest: both max-lifetime shapes (enrollment_failed/agent_timeout and
agent_shutting_down/max_lifetime) now route through the
EnrollmentTimeoutClassifier instead of hard Failed (tenant a53e67ec:
fully provisioned WG Part 2, user never logged in => AwaitingUser).
agent_timeout no longer counts as HasExplicitFailure; failure
webhooks fire only on actual failure verdicts.
3. Maintenance sweep: dropped the too-broad "IsPreProvisioned ne true"
exclusion (sealed devices are protected by their Pending status);
resumed WG Part-2 runs sweep normally with the window anchored to
ResumedAt. New stage-2 silence guard also fixes a latent bug where an
actively-reporting >5h enrollment could be terminalized mid-run.
4. Registration supersede: a fresh registration resolves older
non-terminal sessions of the same device (serial, placeholder-guarded)
as Incomplete("Superseded by ...") with live SignalR updates — closes
the forever-Pending orphan gap when WG Part 2 runs under a new id.
5. Admin retro-reconcile: POST /api/maintenance/reclassify-legacy
(GlobalAdminOnly, DRY-RUN BY DEFAULT) re-runs the classifier over the
pre-classifier "Session timed out" Failed backlog (~495 rows, sample
projected ~87% misdeclared) and resolves Pending orphans. Requires the
new opt-in allowTerminalReclassification override in the status
writer (admin-marked rows stay untouchable).
6. Polish: "0 of N" AccountSetup rollups keep their total (no more
fabricated "0/0" reasons, "never observed" wording when absent);
late-telemetry reconcile heals Incomplete/AwaitingUser to Succeeded
when straggler evidence proves completion; SLA page knows
AwaitingUser/Incomplete ordinals; script_failed on Success/exit-0 now
names the stderr rule in its message.
Tests: backend 2965, DecisionCore 474 (+6 new), agent 1777, web 557 —
all green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
41 lines
1.2 KiB
C#
41 lines
1.2 KiB
C#
using AutopilotMonitor.Functions.Helpers;
|
|
using Xunit;
|
|
|
|
namespace AutopilotMonitor.Functions.Tests;
|
|
|
|
/// <summary>
|
|
/// Guards the registration supersede pass (misclassification audit 2026-07-16): OEM
|
|
/// placeholder serials would cross-match unrelated devices, so they must never be treated
|
|
/// as a device identity.
|
|
/// </summary>
|
|
public class SerialNumberHeuristicsTests
|
|
{
|
|
[Theory]
|
|
[InlineData("PF4DF1EN")]
|
|
[InlineData("5CG5504Y5L")]
|
|
[InlineData(" PF4DF1EN ")] // trimmed
|
|
[InlineData("VMware-56 4d 8a")]
|
|
public void RealSerials_AreUsable(string serial)
|
|
{
|
|
Assert.True(SerialNumberHeuristics.IsUsableSerialNumber(serial));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(null)]
|
|
[InlineData("")]
|
|
[InlineData(" ")]
|
|
[InlineData("0")]
|
|
[InlineData("abc")] // too short to identify a device
|
|
[InlineData("None")]
|
|
[InlineData("UNKNOWN")]
|
|
[InlineData("Default string")]
|
|
[InlineData("To be filled by O.E.M.")]
|
|
[InlineData("System Serial Number")]
|
|
[InlineData("Not Specified")]
|
|
[InlineData("n/a")]
|
|
public void PlaceholderOrEmptySerials_AreNotUsable(string? serial)
|
|
{
|
|
Assert.False(SerialNumberHeuristics.IsUsableSerialNumber(serial));
|
|
}
|
|
}
|