IME's trace listener opens every log once per process with FileMode.Append and FileShare.ReadWrite
and writes at its own stream position afterwards. AgentExecutor.exe (two processes per user-context
script, one per detection script) has such a listener on AgentExecutor.log and on
IntuneManagementExtension.log, so a process that resumes logging after another one appended
overwrites those bytes: the final file holds the last writer, a byte-position tailer has read the
first version. Session 46749560 lost 6 of 16 platform-script results and 4 of 16 executor end
blocks with every tracker health counter at zero — results fell back to the exit code (one Success
where IME said Failed), one finished script stayed Incomplete. The agentexecutor_fallback results
in the fleet are this race, not late IME logging.
The tracker now keeps a fingerprint ledger of every entry it processed from the two multi-writer
files (FNV-1a 64 over the decoded bytes, one CmTraceEntryAssembler shared by the read loop and the
verification so entry boundaries are identical by construction), verifies it on every script end
signal, on a byte-budgeted cadence while an invocation is in flight (256 KB/s, at least 1 s
apart), in a 4 KB guard zone on every growth pass and on a fragment at the bookmark, and rewinds
to the first changed entry; entries whose (offset, hash) still read unchanged are skipped,
everything else is processed as backlog (never fresh, never a calibration anchor). The
policyId-less executor lines are owned by the nearest preceding invocation marker in their file
(PS-AGENT-ARG opens, PS-AGENT-SCRIPT-START upgrades to the platform script, PS-AGENT-INVOCATION and
PS-AGENT-COMPLETED close) instead of a last-started pointer, so an end block a rewind uncovers
reaches its own script. Restart seeds the ledger from the 64 KB behind the persisted bookmark;
a rollover clears it. Four restart-safe counters (ime_overwrite_rewinds,
ime_overwrite_bytes_reprocessed, ime_verify_passes, ime_verified_bytes) ride agent_metrics_snapshot
and ime_pattern_hits — recovered data, not skipped work, no new event type.
Tests: ImeLogTrackerOverwriteRewindTests (15) drive the positional-overwrite write pattern against
both files, including the two field scenarios with the shipped patterns; agent suite 2921 green,
backend known-actions list extended for the two new patterns.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- IME-EXITCODE-CLASS captures the return-code class of the installer exit code ("lpExitCode is defined as <Success|SoftReboot|HardReboot|Retry|Failed>", verified against the IME string literal) as exitCodeClass on AppPackageState; new action captureExitCodeClass, persisted across the agent restart a reboot causes, last value wins across a Retry loop. IME-EXITCODE-MAPPED-FAILED keeps the error path; both match the same line.
- Adapter: exitCodeClass whenever known, exitCode on every terminal state (Installed/Postponed/Skipped/Error) instead of Error only.
- ANALYZE-APP-018 (warning, apps): app_install_completed.exitCodeClass in SoftReboot,HardReboot; +25 HardReboot, +15 observed system_reboot_detected; never fails the session.
- Web: completed-app exit badge amber (failed stays red), "mapped as SoftReboot/HardReboot/Retry" on timeline and Install Progress.
- Tests: pack end to end (phase, current app, class lines, negatives, restart round trip), adapter payloads, linearity samples, backend rule engine + known actions.
- internal gitlink: plan, D-214, backlog.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
IME never writes its "Completed user session" line when the user has no
assigned Win32 apps - its zero-app branch returns before that statement
(ApplicationPoller.cs:713-721, identical in 1.80/1.83/1.97/1.104). So
ImeUserSessionCompletedUtc stayed null, every arm of
ShouldTransitionToAwaitingHello was unsatisfiable, and the 30-min
advisory_completion backstop resolved healthy enrollments as
esp_exit_without_completion_evidence. One tenant: 10 of 30 sessions in a week,
all false positives - DAD-validated desktop, Hello provisioned, RealmJoin still
installing. Every Succeeded session there carries ime_user_session_completed;
not one of the failed ones does.
IME-USER-SESSION-ZERO-APPS reads the line IME does write there and routes it
through the same OnUserSessionCompleted callback. That is IME's own verdict
rather than a weakened gate: the zero-app branch locks the ESP in with an empty
application list, and the sibling emit site returns ProvisioningComplete right
after logging it. Guarded to the AccountSetup phase so it cannot burn the
adapter fire-once flag on a pre-sign-in timestamp, and replayed on the phase
transition when the observation predates the phase marker. AreUserEspAppsSettled
accepts an empty app list only with that explicit observation, never on
emptiness alone.
Second, independent defect: advisory_completion (30 min) overran the RealmJoin
completion gate, which deliberately holds the session and carries its own
bounded resolution (60 min from detection, activity-extended, 4 h cap). One
session installed 25 RealmJoin packages inside the window, the last 2 min before
the failure, while completion_waiting still read "waiting on:
realmjoin_resolution". The handler now re-arms while that gate is closed and its
own deadline is still armed; convergence comes from the gate itself, which
always ends with an Outcome set.
ime_user_session_completed gains an evidence field (zero_user_apps_enumerated /
user_session_completed) - no new event type, so the Constants ->
resource-catalog -> shared-manifests triple stays untouched. The pattern ships
via the config API and reaches existing agents, where an unknown action is a
single Debug line; the behaviour needs the agent release.
Build 0 warnings. DecisionCore 558/558, V2.Core 2530/2530, Backend 4634/4634,
MCP event-types-drift 6/6.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
P0b/P1 of the 2026-08-17 IME code audit (agent release required):
- Tail AppActionProcessor.log (+ rollover glob) in ImeLogTracker and the
--run-ime-matching CLI. IME routes every [Win32App][ActionProcessor]
line there (enforcement decision engine), so IME-NO-ACTION and
IME-NOT-TARGETED-1/2 could never fire before, and GRS skips were
invisible. Live session 0fe2c562 carried 16 no-action and 64
classification lines in that file.
- New patterns (wordings verified in decompiled IME 1.97 AND 1.104):
IME-GRS-SKIP ("is in GRS. The app will not be enforced." -> Postponed;
THE canonical silent non-install reason), IME-GRS-CLEARED (early
subgraph termination, retry next check-in -> Postponed),
IME-TOKEN-FAILURE ("Failed to get AAD token ... errorCode = N").
- New action imeTokenFailure -> ime_token_failure Warning event (new
EventType: Constants + MCP resource-catalog + shared manifests
regenerated). Tracker dedups per distinct errorCode; included in
the historic-replay guard so stale lines from a previous enrollment
cannot emit a misleading warning. Complements IME-TOKEN-SUCCESS:
an auth outage previously left check-ins silently doing nothing.
Tests: adapter emission + unknown-code + tracker dedup; known-actions
catalog extended; all suites green (Functions 4262, Agent 2252, web 762).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two V2 IME fixes, verified against client logs of sessions fc48c71a / 6b4993e5.
Bug 1 — on an ESP terminal failure of the DeviceSetup Apps subcategory the only
device app was stuck mid-install (RealmJoin stuck Downloading 0%) and IME logged
no per-app error, so app_tracking_summary showed 0 failed and the failure was
visible only via the opaque "Apps (Error)" registry string. New
esp_apps_failure_correlation event names the in-flight (IsActive: InProgress/
Downloading/Installing) device app(s) as the likely cause WITHOUT mutating state
(no fabricated app_install_failed) and only on the Apps subcategory.
Bug 2 — AgentExecutor.exe hosts both platform and proactive-remediation scripts
interleaved in the shared AgentExecutor.log; the policyId-less PS-AGENT-EXITCODE /
PS-AGENT-OUTPUT lines from a remediation invocation bled into the last-started
platform script via the sticky pointer (c3e0124c emitted result=Failed but with
exit 0 + the Teams remediation's "[Compliant] No Classic Teams found" stdout).
New PS-AGENT-INVOCATION pattern (resetPlatformScriptContext) clears the platform
line-capture pointer at every "ExecutorLog AgentExecutor gets invoked" boundary,
so a remediation invocation cannot capture into a platform slot; the platform
result stays keyed by policyId via PS-SCRIPT-RESULT.
Agent-only, V2, inert until redeploy; no ConfigVersion bump. New event type added
to the MCP resource catalog (dual-sync). Green: V2.Core 1522, DecisionCore 400,
Backend BuiltInRules 8, MCP vitest 252.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The post-PR-HS1 single-source design (HS-NEW-RESULT JSON only) had a real timing gap:
IME emits the [HS] new result = JSON line only AFTER its batch send to the Microsoft
service, ~30-90 s after the actual script ran (verified in session c1e714e6: 34-74 s
gap per policy). Short Autopilot enrollments often end inside that window, so health-
script rows were entirely missing from completed sessions even though IME had executed
the scripts.
Two-stage approach (matches original V1 timing for the early signal, while keeping
HS-NEW-RESULT as the canonical truth):
- Re-add HS-COMPLIANCE pattern matching BOTH `pre-remdiation` AND `post-remdiation`
detection compliance lines → HandleHealthScriptDetectionResult emits a script_completed
event immediately. Single regex (`(?<part>pre|post)-remdiation`) covers both phases.
- Re-add HS-RUN-CONTEXT / HS-EXITCODE / HS-STDOUT / HS-STDERR patterns feeding a
single-slot `_pendingHealthScript` accumulator (IME runs health scripts sequentially
within a session — verified). HS-COMPLIANCE merges the slot data onto the early-signal
event so exit / stdout / stderr / runContext are populated from the start, not just
the inferred compliance verdict.
- HS-NEW-RESULT continues to fire later with the full JSON (RemediationStatus,
TargetType, ErrorCode, plus the actual remediation phase's data). UI reducer's
dataCompleteness() scoring keeps the more complete entry — early-signal row gets
enriched in place, no duplicate cards.
Remediation phase itself has no early-signal source in IME logs (only the late JSON
exposes Info.RemediationExitCode and RemediationScriptOutputDetails). A follow-up
based on the agentexecutor invocation line's _RemediationScript.output suffix is
possible once we have a real non-compliant cycle log to validate against.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI ajv validation rejected HS-NEW-RESULT.json because the new healthScriptResult
action wasn't in the schema's enum allowlist. Add it next to the other script*
actions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Galactic Admin Mode for Audit Logs: banner + cross-tenant endpoint (galactic/audit/logs)
- Audit page pagination (12 entries per page) with Previous/Next controls
- Platform Metrics: moved computation to backend PlatformMetricsService with 5-min cache
- New endpoint GET /api/galactic/metrics/platform replaces N+1 client-side fetches
- IME log tracker enhancements, DO telemetry pattern, and download progress improvements
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
## Rules as GitHub single source of truth
- Add 75 individual JSON rule files (2 gather, 18 analyze, 55 IME patterns)
under rules/gather/, rules/analyze/, rules/ime-log-patterns/
- Add JSON Schemas for all three rule types with VS Code IntelliSense support
- Add CI combine script (rules/scripts/combine.js) + GitHub Action that
validates schemas with ajv-cli and generates rules/dist/ combined files
- Convert BuiltIn*.cs from hardcoded C# objects to embedded JSON resource loaders
- Add GitHubRuleRepository service to fetch dist/ files from raw.githubusercontent.com
- Add ReseedFromGitHubFunction: POST /api/rules/reseed-from-github?type=all|gather|analyze|ime
## IME Log Patterns
- Extend ImeLogPattern model with Description and IsBuiltIn properties
- Add ImeLogPatterns table + API constants
- Add TableStorageService CRUD methods for IME log patterns
- Add ImeLogPatternService (CRUD, merge, auto-seed from embedded JSON)
- Add ImeLogPatternsFunction: GET/PUT /api/rules/ime-log-patterns (Phase 1: read-only
for tenants, Galactic Admin can enable/disable/edit globally)
- Add IME Log Patterns management page (/ime-log-patterns)
- Wire GetAgentConfigFunction to use ImeLogPatternService instead of static class
## API route consolidation
- All rule endpoints now under /api/rules/* for single certificate exclusion path:
/api/rules/gather, /api/rules/analyze, /api/rules/ime-log-patterns,
/api/rules/reseed-from-github
## Export buttons
- Add per-rule and export-all JSON download buttons to gather-rules,
analyze-rules, and ime-log-patterns pages
## Admin configuration
- Replace old individual reseed buttons with unified "Fetch & Reseed from GitHub"
(calls new GitHub endpoint) plus compact individual-type reseed buttons
- Add CDN cache hint: GitHub raw.githubusercontent.com caches up to 5 minutes
## Preview mode
- ?preview=1 URL param seeds localStorage.previewMode (persists across navigation)
- ?preview=0 clears it; toggle in Navbar settings dropdown (indigo, eye icon)
- IME Log Patterns nav link gated behind galacticAdminMode OR previewMode
- Preview badge shown on nav link when active for normal users
## Tests
- Add xUnit test project (net8.0) with 5 tests for embedded resource loading,
known IME actions validation, and valid categories check
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>