mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 12:42:38 +00:00
feat(web,mcp): the IME overwrite counters surface — session page row "Overwritten log blocks recovered"; get_session_summary coverage carries overwriteRewinds/overwriteBytesReprocessed and a recovered-not-missing note, never a gap (D-246, P2)
The agent (v2.0.1458) counts the log blocks it re-read after a concurrent IME process had overwritten bytes behind its bookmark. The IME Tracker rows on the session page show the count and bytes as their own row, apart from Skipped work; the MCP coverage block adds both counters to sessionTotals and, when rewinds > 0, an imeTracker.note saying the script results and exit codes in those blocks are complete — no coverage.gaps line, no degraded flag, so an assistant does not read recovered data as a blind spot. Sessions from older agents carry neither key and show neither. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -132,7 +132,7 @@ describe('buildSessionCoverage — IME log tracker', () => {
|
||||
ev('ime_tracker_degraded', 50, { file: 'AppWorkload.log', lineBudgetBreaks: 1, regexTimeouts: 0, oversizedLines: 0 }),
|
||||
ev('ime_pattern_hits', 900, { linesRead: 12000, lineBudgetBreaks: 7, regexTimeouts: 2, oversizedLines: 1 }, { severity: 'Debug' }),
|
||||
]);
|
||||
expect(c.imeTracker.sessionTotals).toEqual({ linesRead: 12000, lineBudgetBreaks: 7, regexTimeouts: 2, oversizedLines: 1 });
|
||||
expect(c.imeTracker.sessionTotals).toEqual({ linesRead: 12000, lineBudgetBreaks: 7, regexTimeouts: 2, oversizedLines: 1, overwriteRewinds: null, overwriteBytesReprocessed: null });
|
||||
expect(c.gaps[0]).toContain('skipped the remaining patterns on 7 line(s)');
|
||||
expect(c.gaps[0]).toContain('gave up on 2 line(s) after a regex timeout');
|
||||
expect(c.gaps[0]).toContain('dropped 1 oversized line(s)');
|
||||
@@ -143,9 +143,30 @@ describe('buildSessionCoverage — IME log tracker', () => {
|
||||
ev('agent_started', 0),
|
||||
ev('ime_pattern_hits', 900, { linesRead: 800, lineBudgetBreaks: 0, regexTimeouts: 0, oversizedLines: 0 }),
|
||||
]);
|
||||
expect(c.imeTracker).toEqual({ degraded: false, sessionTotals: { linesRead: 800, lineBudgetBreaks: 0, regexTimeouts: 0, oversizedLines: 0 } });
|
||||
expect(c.imeTracker).toEqual({ degraded: false, sessionTotals: { linesRead: 800, lineBudgetBreaks: 0, regexTimeouts: 0, oversizedLines: 0, overwriteRewinds: null, overwriteBytesReprocessed: null } });
|
||||
expect(c.gaps).toEqual([]);
|
||||
});
|
||||
|
||||
it('reports re-read overwritten log blocks as recovered data — a note, never a gap or degradation', () => {
|
||||
// Agents ≥ 2.0.1458: IME processes overwrite each other's bytes in AgentExecutor.log and
|
||||
// IntuneManagementExtension.log; the tracker re-reads the changed blocks and counts them.
|
||||
const c = buildSessionCoverage(SESSION, [
|
||||
ev('agent_started', 0),
|
||||
ev('ime_pattern_hits', 900, { linesRead: 8957, lineBudgetBreaks: 0, regexTimeouts: 0, oversizedLines: 0, overwriteRewinds: 6, overwriteBytesReprocessed: 5120 }, { severity: 'Debug' }),
|
||||
]);
|
||||
expect(c.imeTracker.degraded).toBe(false);
|
||||
expect(c.imeTracker.sessionTotals).toEqual({ linesRead: 8957, lineBudgetBreaks: 0, regexTimeouts: 0, oversizedLines: 0, overwriteRewinds: 6, overwriteBytesReprocessed: 5120 });
|
||||
expect(c.imeTracker.note).toBe('6 log block(s) a concurrent IME process had overwritten were re-read (5 KB) — script results and exit codes in them are complete, not missing.');
|
||||
expect(c.gaps).toEqual([]);
|
||||
});
|
||||
|
||||
it('adds no note when nothing was overwritten or the agent predates the counter', () => {
|
||||
const c = buildSessionCoverage(SESSION, [
|
||||
ev('agent_started', 0),
|
||||
ev('ime_pattern_hits', 900, { linesRead: 800, lineBudgetBreaks: 0, regexTimeouts: 0, oversizedLines: 0, overwriteRewinds: 0, overwriteBytesReprocessed: 0 }),
|
||||
]);
|
||||
expect(c.imeTracker.note).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildSessionCoverage — collectors, upload, disk', () => {
|
||||
|
||||
@@ -46,6 +46,7 @@ export const COVERAGE_EVENT_FIELDS =
|
||||
'data.bootToAgentStartSeconds,data.agentUptimeSeconds,data.outcome,data.note,' +
|
||||
'data.earliestRejectedSourceTimestamp,' +
|
||||
'data.file,data.firstSkippedPattern,data.lineBudgetBreaks,data.regexTimeouts,data.oversizedLines,data.unanchoredPatterns,data.linesRead,' +
|
||||
'data.overwriteRewinds,data.overwriteBytesReprocessed,' +
|
||||
'data.collector,data.reason,data.errorType,' +
|
||||
'data.pendingItemCount,data.pendingBytes,data.kind,data.itemCount,data.disk_free_gb,' +
|
||||
'data.truncated,data.includedFiles,data.includedBytes,data.skippedFiles,data.skippedByReason,data.problemsByKind,' +
|
||||
@@ -87,7 +88,15 @@ export interface ImeTrackerCoverage {
|
||||
lineBudgetBreaks: number | null;
|
||||
regexTimeouts: number | null;
|
||||
oversizedLines: number | null;
|
||||
/**
|
||||
* Times the tracker re-read a log block a concurrent IME process had written over bytes
|
||||
* it had already read (agents ≥ 2.0.1458). Recovered data — never a gap and not degradation.
|
||||
*/
|
||||
overwriteRewinds: number | null;
|
||||
overwriteBytesReprocessed: number | null;
|
||||
};
|
||||
/** Present when overwriteRewinds > 0: says in words that those blocks were recovered, not lost. */
|
||||
note?: string;
|
||||
}
|
||||
|
||||
export interface CollectorDegradation {
|
||||
@@ -276,8 +285,20 @@ export function buildSessionCoverage(
|
||||
lineBudgetBreaks: num(hits.data?.lineBudgetBreaks),
|
||||
regexTimeouts: num(hits.data?.regexTimeouts),
|
||||
oversizedLines: num(hits.data?.oversizedLines),
|
||||
overwriteRewinds: num(hits.data?.overwriteRewinds),
|
||||
overwriteBytesReprocessed: num(hits.data?.overwriteBytesReprocessed),
|
||||
}
|
||||
: undefined;
|
||||
// IME processes append to AgentExecutor.log and IntuneManagementExtension.log with independent
|
||||
// stream positions and overwrite each other; the tracker re-reads what changed. That is
|
||||
// recovered data — it must never read as a gap or as a degraded tracker.
|
||||
const rewinds = sessionTotals?.overwriteRewinds ?? 0;
|
||||
const overwriteNote =
|
||||
rewinds > 0
|
||||
? `${rewinds} log block(s) a concurrent IME process had overwritten were re-read` +
|
||||
`${sessionTotals?.overwriteBytesReprocessed ? ` (${Math.round(sessionTotals.overwriteBytesReprocessed / 1024)} KB)` : ''}` +
|
||||
' — script results and exit codes in them are complete, not missing.'
|
||||
: undefined;
|
||||
let imeTracker: ImeTrackerCoverage;
|
||||
if (degradedEv) {
|
||||
imeTracker = {
|
||||
@@ -309,6 +330,7 @@ export function buildSessionCoverage(
|
||||
} else {
|
||||
imeTracker = { degraded: false, ...(sessionTotals ? { sessionTotals } : {}) };
|
||||
}
|
||||
if (overwriteNote) imeTracker.note = overwriteNote;
|
||||
|
||||
// ── Collectors ──────────────────────────────────────────────────────────────
|
||||
const collectors: CollectorDegradation[] = [];
|
||||
|
||||
@@ -220,6 +220,10 @@ export default function DeviceDetailsCard({ events, latestAgentVersion, session
|
||||
];
|
||||
const imeSkipParts = imeSkipCounters.filter(([, v]) => Number(v) > 0).map(([k, v]) => `${v} ${k}`);
|
||||
const imeSkips = imeSkipParts.length;
|
||||
// Log blocks a concurrent Intune process wrote over after the agent had read them, re-read by
|
||||
// the agent (agents ≥ 2.0.1458). Recovered data — shown apart from "Skipped work" on purpose.
|
||||
const imeOverwriteRewinds = patternHits?.overwriteRewinds ?? imeTracker?.ime_overwrite_rewinds;
|
||||
const imeOverwriteBytes = Number(patternHits?.overwriteBytesReprocessed ?? imeTracker?.ime_overwrite_bytes_reprocessed ?? 0);
|
||||
const apiLatencyMs = session?.avgApiLatencyMs ?? 0;
|
||||
const hasConnectivity = apiLatencyMs > 0 || bandwidthEstimate || metricsSnapshot;
|
||||
|
||||
@@ -676,6 +680,17 @@ export default function DeviceDetailsCard({ events, latestAgentVersion, session
|
||||
? "Matching was cut short on some log lines — pattern coverage may be incomplete for this session. Nothing to fix on your side; use Report Session if the stats look wrong."
|
||||
: undefined}
|
||||
/>
|
||||
{imeOverwriteRewinds !== undefined && (
|
||||
<DetailRow
|
||||
label="Overwritten log blocks recovered"
|
||||
value={Number(imeOverwriteRewinds) > 0
|
||||
? `${Number(imeOverwriteRewinds)}${imeOverwriteBytes > 0 ? ` (${formatBytesCompact(imeOverwriteBytes)} re-read)` : ""}`
|
||||
: "none"}
|
||||
title={Number(imeOverwriteRewinds) > 0
|
||||
? "Intune processes wrote over log lines the agent had already read; the agent re-read them, so script results and exit codes in those blocks are complete. Nothing to fix on your side."
|
||||
: undefined}
|
||||
/>
|
||||
)}
|
||||
{(patternHits?.unanchoredPatterns ?? imeTracker?.ime_unanchored_patterns) !== undefined && Number(patternHits?.unanchoredPatterns ?? imeTracker?.ime_unanchored_patterns) > 0 && (
|
||||
<DetailRow label="Unanchored patterns" value={String(patternHits?.unanchoredPatterns ?? imeTracker?.ime_unanchored_patterns)} />
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user