Skip to content

feat(intune): model Win32 app deployment transactions (#357) - #525

Merged
adamgell merged 42 commits into
mainfrom
lane/intune-357-win32
Aug 10, 2026
Merged

feat(intune): model Win32 app deployment transactions (#357)#525
adamgell merged 42 commits into
mainfrom
lane/intune-357-win32

Conversation

@adamgell

@adamgell adamgell commented Aug 8, 2026

Copy link
Copy Markdown
Owner

What is done

Implements cmtraceopen_parser::intune::apps::windows::win32, the canonical evidence-backed transaction view of an Intune Win32 app deployment across assignment, applicability, requirements, dependencies, content, detection, enforcement, post-install detection, and reporting.

New modules under crates/cmtraceopen-parser/src/intune/apps/windows/win32/:

  • models.rs: public types on top of intune::evidence; Win32Outcome distinguishes all 14 required states (not targeted, not applicable, requirement failed, dependency unresolved, detection already satisfied, content unavailable, content delivery/hash/staging failure, enforcement command failure, installer-reported failure, installed-but-not-detected, reporting failure, success, deferred/retry, insufficient evidence) plus in-flight Assigned/Enforcing.
  • sources.rs: artifact classification (IntuneManagementExtension.log, AppWorkload.log, AppActionProcessor.log, rotations including underscore/numbered variants, AgentExecutor.log, supplemental installer output) and coverage state.
  • signals.rs: per-logical-record classification with typed known/unknown return-code tokens (Win32ReturnCodeKind::Unmapped instead of invented meaning).
  • reducer.rs: identity-keyed reduction into immutable snapshots. Identifiers outrank time proximity; unkeyed/malformed records and physical-line fragments cannot create or terminate a transaction; supplemental MSI/PSADT/Burn/EXE evidence attaches only when a keyed record names it.
  • rules.rs: derive_findings with severity and confidence separated from cause classification; every finding cites exact evidence, last confirmed phase, and the smallest next artifact request; high-confidence failure requires terminal or corroborating evidence.
  • redaction.rs: deterministic default-safe export projection; raw user/execution context, UPNs, and local paths are redacted by default.

Seam decision: raw records are framed by the shared CCM parser via intune::ime_parser::parse_ime_content — no second raw CCM parser. intune::event_tracker, download_stats, and timeline keep their public APIs and behavior unchanged; this module is the single owner of the transaction view. Semantic classification runs only after logical-record framing.

Fixture matrix (16 scenarios, all synthetic, under crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/): complete-success, already-installed-detection, requirement-failure, dependency-failure, no-usable-content, hash-or-staging-failure, installer-known-nonzero-code, installer-unknown-code, installed-but-not-detected, reporting-failure-after-local-outcome, retry-without-terminal-outcome, incomplete-bundle-missing-appworkload, rotation-split-record, same-minute-unrelated-apps, unkeyed-malformed-record, privacy-redaction. Each has manifest.json ("syntheticFixture": true), evidence logs carrying the SYNTHETIC FIXTURE marker, and expected.json golden serialization.

Deliberately not done

  • No changes to src-tauri native known-source discovery or bundle collection; current discovery already names the primary artifacts, and the issue allows deferring native changes to a separate commit. None was needed.
  • No changes to MSI/PSADT/Burn parsers; they remain independent supplemental formats with their public APIs intact.
  • No re-export shims moving event_tracker/download_stats/timeline under the new namespace; they are wrapped conceptually (kept as-is with one behavior owner) rather than relocated, per the "wrap, do not reimplement" allowance during migration.
  • Pre-existing cargo fmt --check drift on main (55 diffs in files this lane never touched, e.g. collector/profile.rs, intune_skeleton_contract.rs) is left alone; lane files are fmt-clean.

Assumptions

  • The fixture root follows the sibling lanes' actual layout tests/fixtures/intune/apps/windows/win32/ (microsoft-store and scripts convention) rather than the issue text's tests/fixtures/intune/windows/win32/; conventions in the tree win over the issue's path sketch.
  • "Wrapped or moved with one behavior owner" is satisfied by keeping the legacy IME modules' behavior untouched and making this module the sole transaction authority, since existing consumers must stay compatible.
  • A nonzero code in the embedded return-code table is reported as the table's classification (e.g. success-with-reboot) rather than assumed failure; unknown decimal/hex codes are surfaced as unmapped tokens.

Verification (run from the worktree root)

  • cargo test --locked -p cmtraceopen-parser: 2338 passed, 0 failed (871 unit, 1461 across 46 integration suites, 6 doc-tests)
  • cargo test --locked -p cmtraceopen-parser --test intune_windows_win32: 21 passed, 0 failed
  • cargo clippy --locked -p cmtraceopen-parser --all-targets -- -D warnings: clean
  • cargo check --workspace: clean
  • cargo check --locked -p cmtraceopen-parser --target wasm32-unknown-unknown: clean
  • git diff --check: clean
  • cargo fmt --check --all: 55 pre-existing diffs on main, none in files this PR touches

Hardening round (consolidated multi-agent review)

Every finding of the consolidated review is mapped to a fix commit or a deliberate deferral.

# Finding Resolution
1 No session/attempt boundary; max-rank fold lets unrelated later success overwrite proven failure Fixed in b6c517e0. Terminal statements are equal-authority candidates; the only linkage that orders two of them is the same artifact's own record numbers or strictly-later trusted timestamps both records stated. Linked retry success supersedes but the failure survives as superseded_failures + a win32-superseded-failure finding; unordered contradictions become the new conservative Win32Outcome::Conflicting.
2 ReportingFailed absorbing; RetryScheduled masks later ContentDeliveryFailed Fixed in b6c517e0. ReportSubmitted clears a ReportingFailed candidate it is explicitly ordered after; retries are in-flight (pending) states that can never contest a terminal candidate recorded before or after them.
3 Coverage gaps do not stay gaps (empty/misclassified artifact closes the expected-artifact check) Fixed in e0ec95f3. Only usable evidence (confirmed content, or a caller-declared gap that carries its own entry) satisfies the expected-artifact check; the synthesized Missing entry, the finding, and the confidence cap all survive.
4 Caller input order in the sort tie-break; all-or-nothing trustworthiness Fixed in b6c517e0. artifact_index removed from every sort key; fallback is canonical (artifact_id, record_number). Permutation and duplication adversarial tests added. Timestamp trust stays all-or-nothing for the phase fold with the reason documented (a total sort over a partial order needs an arbitrary tie-break); outcome resolution uses the partial order per-record via sequenced_after.
5 reconcile_partial_keys inference promotes confidence to High Fixed in e0ec95f3. Inferred key components are flagged and complete the key without counting toward confidence_for's High; only a stated deployment type does.
6 stable_token is a new unsalted cross-export identifier Accepted status quo per instruction (ADR-004 token scope is provisional): the win32-local copy is deleted in 3ea90177 and the module reuses common's existing token behavior, adding no new token API. Defining the equality-scope contract remains the Store pilot's task.
7 Findings are never redacted Fixed in 3ea90177. Every finding summary passes through the shared redaction grammar at the single push() choke point; the fixture harness now scans derived findings with the same needles as the projection.
8 Redaction fork reintroduces fixed bugs and misses secret shapes Fixed in fd8b026d (shared module, own commit: account fields bounded like the path rule, MSI-property credentials, / sigil, SIDs, hostname fields + UNC hosts, tenant-id fields) and 3ea90177 (win32 collapses onto common::redact_text; JSON-escaped path leak pinned by test; privacy fixture extended with SID + hostname + MSI secret + space-containing account via the new manifest-declared privacyProbes, which the harness only accepts when each probe is pinned by a redactionMustNotContain needle).
9 One behavior owner unmet (three disjoint grammars) Fixed (minimal wrap) in 8408ec94. download_stats owns the content-download vocabulary via pub(crate) accessors that win32 composes exactly as download_stats does; guid_registry owns the GUID shape for both event_tracker and win32; ownership notes added to both legacy modules; day-one drift closed (DownloadStalled recognized, deliberately non-terminal). The retry vocabulary is deliberately not reused, documented at the site.
10 4 of 14 outcome states uncovered Fixed in 780dc210. New scenarios not-targeted, not-applicable, enforcement-command-failed, insufficient-evidence; SCENARIOS pin grows 16 → 20 with the corpus-equality test enforcing it.
M1 finding_id omits execution context; id grammar drift Fixed in 0f3e84b1 (full key incl. lowercase context segment; all 20 fixture expectations updated).
M2 push_unknown_vocabulary cites unrelated records Fixed in 0f3e84b1 via Win32Observation::enforcement_shaped_but_unmatched.
M3 Metadata join ignores deployment_type_id Fixed in 0f3e84b1 (scoped entry wins, app-wide applies, other-deployment-type never labels).
M4 Return-code table lacks a deployment-type key Fixed in 0f3e84b1 (Win32ReturnCodeMapping::deployment_type_id, serde-defaulted, exact scope wins).
M5 Capped artifacts can set terminal Succeeded Decided + documented in 0f3e84b1: framed records in a truncated artifact are authentic evidence, so the terminal outcome stands, but the cap is a coverage gap and High is unreachable (pinned by test).
M6 pub mod everything, double public paths, inverted signals/rules naming Fixed in ea7ad7da (pure rename signals.rsrules.rs, rules.rsfindings.rs; private mods + curated pub use; reference sweep per the no-semantic-search checklist recorded in the commit message).
M7 No input caps; as u32 truncation; Default yields invalid schema_version; no #[serde(default)] Fixed in 0f3e84b1 (MAX_RECORDS_PER_ARTIFACT with analyzer-set Capped coverage on overflow; checked u32::try_from; Default yields the current schema version and additive fields are serde-defaulted).
M8 Fixture gaps (degraded-artifact scenario, true-negative scenario, thin privacy surface) Partially addressed / deferred: privacy surface extended (finding 8) and insufficient-evidence is a no-verdict scenario; a dedicated capped/accessDenied/parseFailed degraded-artifact corpus scenario and a full true-negative scenario are deferred — the behaviors are covered by unit tests (a_capped_artifact_proves_a_terminal_outcome_only_at_demoted_confidence, a_declared_unreadable_artifact_reports_its_own_coverage_status) but not yet by corpus fixtures.

Deferrals, explicitly: (a) the ADR-004 stable-token equality-scope contract (owned by the Store pilot per instruction); (b) corpus-level degraded-artifact and true-negative scenarios (behavior covered by unit tests, fixtures deferred to keep this round reviewable); (c) per-record timestamp trust inside the phase fold (documented at order_records — the partial order is honored where it is safe, in outcome resolution).

Hardening verification (after ea7ad7da)

  • cargo test -p cmtraceopen-parser: 2246 passed, 0 failed
  • cargo clippy -p cmtraceopen-parser --all-targets -- -D warnings: clean
  • cargo check --workspace: clean
  • cargo check -p cmtraceopen-parser --target wasm32-unknown-unknown: clean

Closes #357

🤖 Generated with Claude Code

Review fix round 2

13 verified findings, fixed test-first. Every behavioral fix landed with a failing regression test before the change.

# Finding Resolution
1 surviving[0] panic: a sequenced_after 3-cycle eliminates every terminal candidate Fixed in 4f26ef4e: empty survivor set reduces to Conflicting with no superseded entries (supersession requires a surviving eliminator). Test: a_sequencing_cycle_across_artifacts_stays_conservative_instead_of_panicking.
2 win32 lacked download_stats' transition-template ignore gate, minting terminal ContentDeliveryFailed from template lines Fixed in 6906d7d3: gate exposed as is_state_transition_template (one owner) and checked before every composed download check. Tests: the_state_transition_template_never_mints_a_download_signal (win32, incl. the exact line download_stats pins).
3 Refactor dropped pre-consolidation download phrasings (has failed / state: Failed / result = Failed / is complete(d) / complete / Started the download / Start content download) Fixed at the owner in 6906d7d3: download_stats regexes widened so both consumers gain the recall. Tests: every_pre_refactor_download_phrasing_still_classifies (win32) + the_shared_vocabulary_carries_the_pre_consolidation_ime_phrasings (owner pin).
4 Detection pre/post-enforcement decided by fold order, i.e. by the caller's artifact-id sort Fixed in 4f26ef4e: detection arms judge by explicit sequenced_after linkage against precomputed EnforcementLinkage; unprovable positions mint nothing and stay cited evidence. Test: detection_diagnosis_is_decided_by_linkage_not_artifact_id_sort_order (id-rename invariance + no unproven DetectedBeforeEnforcement).
5 Readable-but-unconfirmed artifact reported coverage Available Fixed in ecb99f55: downgraded to ParseFailed in the one coverage entry; degraded_coverage, win32-coverage-unusable-artifact, and confidence all read it. Test: a_content_misclassified_artifact_is_not_reported_available.
6 Return code/kind/reboot were the fold's last write; a 1618-Retry completion clobbered the winning cycle's attribution Fixed in 2e216bc5: exported triple comes from the winning completion candidate (winning_attribution); retry-kind completions mint no candidate. Tests: a_retry_kind_completion_does_not_clobber_the_outcome_bearing_return_code (1603 kept, unmapped finding fires), a_retry_kind_completion_after_a_reboot_success_keeps_the_winning_attribution. Divergence: the round-2 spec said 3010-then-1618 should export reboot_required: false; deriving the flag from the winning 3010 candidate (the stated mechanism, and the field's documented contract "true only when a reboot code was observed") yields true, and false is reachable only via exactly the last-write coupling this finding removes. The test pins true.
7 account_field_re/host_field_re re-hashed values beginning with an emitted token plus prose Fixed in 3c74cc33 via the allowed equivalent guard: starts_with_token in both closures preserves token+prose whole; the character-class form was rejected because the module pins that malformed token-lookalikes must still be masked (malformed_mask_tokens_are_not_treated_as_already_masked). Stale doc comments rewritten. Tests: an_account_value_starting_with_a_token_keeps_the_token_and_the_prose, a_host_value_starting_with_a_token_is_not_rehashed.
8 Documents and Settings dropped from the profile-path alternation Fixed in 3c74cc33, keeping the [\\/]{1,2} single-or-JSON-escaped form for both roots. Test: a_documents_and_settings_profile_path_is_masked (raw + JSON-escaped).
9 Same-basename installer artifacts collapsed (map overwrite); survivor corroborated on a guess Fixed in ecb99f55: basename maps to every artifact id; a reference links only when unambiguous; all candidates stay visible in unlinked_installer_artifacts (sorted for ADR-003 canonicality). Test: same_basename_installer_artifacts_never_collapse_or_link_ambiguously.
10 privacyProbes: substring coverage counted as coverage; strip ran over descriptors; non-array declaration silently ignored Fixed in 318809d3: probes must be declared verbatim as needles; descriptors are scanned with only the two declaration fields removed (leaks in outputs stay detectable); non-array/non-string/empty declarations fail loudly. Fixture updated to the tightened contract. Mutation tests: a_probe_covered_only_by_a_substring_needle_is_rejected, a_non_array_privacy_probes_declaration_is_rejected, a_probe_leaking_into_a_descriptor_output_field_is_detected.
11 Cap enforced by parse-everything-then-truncate; Capped detail claimed records "were not read" Fixed in ecb99f55: parse_ime_content_bounded stops framing at the bound (record, fragment, and fallback paths), making the detail true. Tests: bounded_parsing_stops_at_the_limit_and_reports_the_remainder, bounded_parsing_bounds_the_fallback_and_fragment_paths_too.
12 DownloadCompleted could not clear an earlier DownloadFailed the way ReportSubmitted clears ReportingFailed Fixed in 2e216bc5: one generalized Clearance mechanism (positive statement supersedes one signal's candidates it is ordered after); cleared failures survive as superseded_failures for reporting and download alike; hash/staging failures deliberately not cleared. Tests: a_download_completed_after_a_download_failure_clears_it_like_reporting, a_download_completed_does_not_clear_a_hash_or_staging_failure.
13 #[serde(default = "default_schema_version")] compatibility fallback Removed in c8ab6236 (AGENTS.md forbids fallbacks; nothing deserializes the type; sibling StoreAnalysis has none).

ff707198 carries two clippy fixups for the batch.

Round 2 verification (after ff707198)

  • cargo test -p cmtraceopen-parser: 48 suites, 0 failures (915 lib tests + integration)
  • cargo clippy -p cmtraceopen-parser --all-targets -- -D warnings: clean
  • cargo check --workspace: clean
  • cargo check -p cmtraceopen-parser --target wasm32-unknown-unknown: clean
  • npx tsc --noEmit: clean

Review fix round 3

12 verified findings plus below-cap items, fixed test-first in eight reviewable slices. Findings 1, 3, and 4 were treated as one class per the review directive: survivorship was re-derived independently by resolve_outcome, winning_attribution, the fold-time local_outcome snapshot, and collect_superseded, and the four answers could disagree.

# Finding Resolution
1 winning_attribution was survivorship-blind and used a max tie-break where the outcome pick used min Fixed in 40cf9c0f: one Resolution struct computed in one pass. Attribution is the earliest-canonical surviving completion stating the resolved outcome that no differently-concluding candidate is transitively ordered after; when a non-completion candidate decided the outcome, no return token is exported. One comparator (SequenceKey::canonical) serves the outcome pick and the attribution pick. Tests: a_superseded_reboot_completion_does_not_leak_its_return_code, attribution_follows_linkage_not_artifact_id_sort_order.
2 An unplaceable DetectionNotSatisfied vanished into a silent clean Succeeded Fixed in 1f9b60d3: Win32Transaction.unlinked_detection_observations (additive field) carries the unplaced records; win32-unlinked-detection-failure (Warning, Medium) cites them; next_evidence_request now keys on more than the outcome and names the timestamp/journal linkage evidence that would place the verdict. InstalledNotDetected is still never minted without proof. Tests: an_unplaceable_not_detected_record_is_surfaced_instead_of_a_silent_succeeded, a_provably_placed_not_detected_record_is_not_flagged_as_unlinked.
3 Elimination by a non-survivor: a cycle member falling to another cycle member let an independent candidate win, with false "explicitly ordered" supersession text Fixed in 40cf9c0f: CandidateOrder closes sequenced_after transitively; a candidate is superseded only by a contender that itself survives, so cycle members stay contenders and the knot resolves Conflicting with no supersession claims. Test: a_cycle_does_not_let_an_independent_candidate_win_or_mint_false_supersessions (3-cycle + independent survivor, both input orders).
4 Stale fold-time local_outcome snapshot Fixed in 40cf9c0f: the local outcome under ReportingFailed is recomputed as the surviving outcome of the non-reporting candidates (same machinery, cleared candidates excluded). Tests: a_cleared_delivery_failure_leaves_no_stale_local_outcome_under_a_reporting_failure (was exporting the cleared failure), a_reporting_failure_after_a_retry_success_reports_the_surviving_local_outcome.
5 Template gate only guarded the download vocabulary Fixed in 5eda8589: is_state_transition_template hoisted to the top of classify_signal — a template line returns Unclassified before any content/hash/staging/installer/detection rule and never raises unknown-vocabulary, matching download_stats' own composition. Test: transition_templates_are_bookkeeping_for_every_rule_not_just_downloads (Hash Mismatch, Installation-is-done, staging and detection To:-field variants).
6a/10 download_start_re classified "failed to start download" as a start Fixed in 31bdd8c9: no lookbehind in the regex crate, so is_download_start matches the vocabulary and rejects a preceding failure verb in code (negated_start_re); the phrase is honest failure vocabulary and now lives in download_failed_re, so a lone failed-start line yields one failed DownloadStat and win32 classifies DownloadFailed. Both consumers share the one predicate. Tests: a_failed_start_is_a_download_failure_never_a_start, an_ordinary_start_line_still_asserts_a_start, a_failed_download_start_classifies_as_a_failure_not_a_start (win32).
6b src-tauri is_empty() gate collapsed the downloads panel on one spurious stat Fixed in 5aa98f20 (separate src-tauri commit): merge_synthesized_downloads fills in only the content ids the extractor produced nothing for; extracted stats win per id. Tests: one_spurious_extracted_stat_does_not_suppress_synthesized_downloads, merge_synthesized_downloads_still_covers_the_no_extraction_case.
7 Duplicate caller artifact ids fused two files into one journal and collided observation ids Fixed in 17022816: disambiguate_artifact_ids suffixes later holders (id#2, ...) at ingestion, mirroring the Store sibling's unique_observation_id; the all-unique case borrows. Test: two_inputs_sharing_an_artifact_id_are_not_one_journal (no cross-file record-number supersession, distinct ids).
8 Account rule hashed bracketed prose whole; token-prefixed values leaked their trailing identity Fixed in a725c070: (a) the value pattern fires on a leading [ only for token-shaped values, restoring main's behavior for [not signed in] prose while malformed token-lookalikes are still masked; (b) preserve_token_mask_tail keeps the token and masks the non-empty remainder for account and host rules, idempotently. is_token_body dedupes the validity predicate. Docs updated; scripts/remediations goldens stayed green. Tests: a_bracketed_prose_account_value_is_not_hashed_whole, an_account_value_starting_with_a_token_keeps_the_token_and_masks_the_tail (replaces the round-2 keep-the-prose pin: the trailing identity leak was the pre-existing bug that pin froze), a_host_value_starting_with_a_token_masks_its_trailing_fragment.
9 Harness removed the whole redactionMustNotContain array before the prohibition scan Fixed in 5c790ef5: only declared probe strings are stripped from within the two declaration arrays; non-probe needles stay scanned. Test: a_non_probe_needle_carrying_forbidden_material_is_rejected (a fake C:\Users\ needle without a matching probe now fails validation).
11 build_line_starts scans past the bounded region; cap docs overclaimed Doc option taken in a75a01ff: parse_ime_content_bounded, parse_ime_records, and MAX_RECORDS_PER_ARTIFACT now state exactly what is bounded (entry materialization — framed records and fragments both count) and what is not (the single O(bytes) newline index kept for exact line numbers). Bounding the index itself would trade exact line numbers on capped tails for a scan that is already linear and allocation-free.
12 Capped and ParseFailed collided: ParseFailed status with the cap's detail Fixed in a75a01ff: one match reconciles them — ParseFailed dominates and the single detail states both facts. Test: a_capped_misclassified_artifact_reports_one_reconciled_status_and_detail.

Cleanups folded in: TerminalCandidate.reboot_required derived from the kind; push_terminal wrapper chain collapsed; the duplicated Clearance/TerminalCandidate sequence triple folded into SequenceKey; collect_superseded compares borrowed &str; attribution work skipped entirely on Conflicting; the pre-consolidation phrase tables shared via download_stats::test_vocabulary.

Fixture updates (justified in 40cf9c0f): installed-but-not-detected and reporting-failure-after-local-outcome now expect no top-level return token — their outcomes are decided by non-completion records, and the completion's story survives via localOutcome/superseded evidence.

Round 3 verification (after a75a01ff)

  • cargo test -p cmtraceopen-parser: 48 suites, 0 failures (929 lib tests + integration)
  • cargo clippy -p cmtraceopen-parser --all-targets -- -D warnings: clean
  • cargo check --workspace: clean
  • cargo check -p cmtraceopen-parser --target wasm32-unknown-unknown: clean
  • cargo test (src-tauri): 29 suites, 0 failures
  • cargo clippy -- -D warnings (src-tauri): clean

Review fix round 4 (CodeRabbit)

18 CodeRabbit threads verified against the code: 17 fixed (behavioral fixes test-first), 1 declined with rationale on the thread.

# Finding Resolution
1 split_leading_token re-found the ] and asserted it with expect Fixed in c32f6999: the closing bracket is located once and validated in place; no expect on parsed log data.
2 Linear Vec::contains inside O(observations) filters (evidence_for_signal, push_unkeyed_signals, push_unlinked_detection_failure) Fixed in c32f6999: one BTreeSet membership set per call.
3 Schema docs claimed enum variant additions are additive Fixed in c32f6999: a public enum variant addition is documented as breaking (serde unknown-variant rejection; no #[non_exhaustive] per family convention); Win32Phase documents variant order as a load-bearing contract; Win32Outcome::Assigned doc widened to match the reducer, resolving the rotation-split-record outcome/phase apparent contradiction.
4 parse_code("-0x5") exported decimal -5 next to hex 0x5 Fixed in 95734e8b (test first): negative hex routes through the same signed-to-u32 rendering as the decimal branch; out-of-i32-window negatives get no 32-bit hex view.
5 Negation gate suppressed more phrasings than the failure vocabulary carried, so "Could not start the content download" vanished Fixed in 4e1cfa93 (test first): download_failed_re carries the exact negation x start-verb alternation; shared NEGATED_START_FAILED table pins the pairing in download_stats and the win32 rules tests.
6 Two in-diff GUID regexes still embedded the full GUID shape Fixed in b1abaffd: content_id_re and extract_policy_id compose from GUID_PATTERN. The sibling family modules' duplicate consts are merged code outside this lane and left to a merged-surface audit.
7 win32 redaction tests tested the shared grammar, never the projection; three near-duplicates; no malformed-lookalike pin Fixed in f7413ba4: grammar tests moved to common/redaction.rs with their owner (duplicates collapsed); the module now tests redact_observation/redact_transaction/redacted_export_projection (Sensitive masked field-by-field, Public verbatim, keys survive, projection idempotent under its honest name); new pin a_malformed_token_lookalike_is_masked_not_trusted covers uppercase-kind and wrong-length-hash lookalikes.
8 Path-probe needle could never match the serialized export (re-escaped backslashes), so the strongest privacy assertion was vacuous Fixed in 948a9f40 (harness side): needles are matched against unescaped string leaves of the redacted analysis and derived findings; pins prove both the old vacuity and the new detection.
9 Evidence privacy scan deleted probe substrings before scanning, so a bare-prefix probe hid undeclared paths; escaping was backslash-only Fixed in 948a9f40: privacy_problems_excluding_probes exempts whole probe values only (raw + serde-escaped occurrence ranges), profile-path matches extend through the profile-name segment, SIDs exempt by covered range, emails by whole-value containment. Descriptor validation unchanged.
10 Missing/misspelled counter keys defaulted to 0/false and asserted nothing Fixed in 948a9f40: counter and bool expectation keys are required and type-checked.
11 rotation.fragmentComplete was consumed by nothing, so stale flags were undetectable Fixed in 948a9f40: the flag is bound to the physical framing truth of each captured IME artifact; the binding caught a third stale flag the review had not named (unkeyed-malformed-record); all three now false.
12 Three fixtures omitted finding confidence; insufficient-evidence assertion said "smallest artifact" while requesting two Fixed in 948a9f40: "confidence": "high" asserted in all four fixtures; assertion reworded to "minimum artifact set".
13 Merged downloads sorted by timestamp text, reversing chronology across a year boundary Fixed in 1036f8af (test first, src-tauri): sort by timestamp_epoch with text and content id as deterministic tie-breakers.
14 pub use models::*; contradicts a curated surface Declined with rationale on the thread: it is the family convention (microsoft_store, remediations, scripts export models identically), and models.rs is a curated public model file whose items are all deliberately part of the schema-version contract; switching only win32 would fork the family shape.

dc66cacc formats only the code this round added; pre-existing fmt drift stays untouched.

Round 4 verification (after dc66cacc)

  • cargo test -p cmtraceopen-parser: 48 suites, 0 failures (943 lib tests + integration)
  • cargo clippy -p cmtraceopen-parser --all-targets -- -D warnings: clean
  • cargo check --workspace: clean
  • cargo check -p cmtraceopen-parser --target wasm32-unknown-unknown: clean
  • cargo test (src-tauri): 29 suites, 0 failures
  • cargo clippy -- -D warnings (src-tauri): clean
  • npx tsc --noEmit: clean
  • git diff --check: clean

Hermes review fixes (round 5, after 35aadf72)

Fixes for the "Hermes charter review" comment, TDD (RED with Hermes's exact counterexamples first), one commit per finding.

# Finding Disposition Commit
1 High: bundle-wide execution-context inference merged unrelated transactions Fixed. reconcile_partial_keys no longer promotes an Unknown context to the bundle's single observed context; a context is set only by the record itself or its own execution block, and an unknown context keys its own transaction. Regression: a_context_less_record_never_adopts_the_bundles_observed_context. Deployment-type uniqueness inference in the same function was audited against the same discipline and deliberately retained: a deployment type id is a stable configuration identity subordinate to an exactly matched app id (moderate strength under ADR-002), it is flagged deployment_type_inferred, and it never raises confidence (ADR-001); the rationale is now in the function's Rustdoc. The privacy-redaction fixture relied on the promotion (its IME policy record states no context) and now honestly expects a second, unknown-context transaction. 9da4acc4
2 High: block-key propagation spread first-seen deployment type / context past a conflict Fixed. apply_block_key now mirrors the app-id conflict rule per component: an app conflict still refuses to key the block at all; a deployment-type or context conflict withholds that component, so unkeyed records keep a partial key instead of a first-wins guess while uncontested components still spread. Regressions: a_block_with_conflicting_deployment_types_refuses_to_spread_one, a_block_with_conflicting_execution_contexts_refuses_to_spread_one. b17e959d
3 Medium: derive_findings underdocumented as a published-crate API Fixed. Rustdoc now states the contract: immutable reduced snapshot input (no I/O, no live state), deterministic ordering (fixed rule order, canonical key-sorted transaction order, stable finding ids), and evidence semantics (every finding evidence-backed, independent severity/confidence axes, summaries pass the shared redaction grammar). 79a27e82

Config change disclosure

35aadf72 syncs .coderabbit.yaml byte-for-byte from origin/main (zero diff vs main). This lane branched before main's 96b841cc, so its copy still had drafts: false (plus request_changes_workflow: false, auto_apply_labels: false), and CodeRabbit reported "Review skipped: draft pull request" on this draft PR, blocking incremental reviews and the approval the merge gate requires. The commit touches nothing else.

Round 5 verification (after 35aadf72)

  • cargo test --locked -p cmtraceopen-parser --no-fail-fast: 49 suites, 2293 passed, 0 failures
  • cargo clippy --locked -p cmtraceopen-parser --all-targets -- -D warnings: clean
  • cargo clippy --workspace --all-targets -- -D warnings: clean
  • cargo check --workspace: clean
  • cargo check -p cmtraceopen-parser --target wasm32-unknown-unknown: clean

Summary by CodeRabbit

  • New Features

    • Added comprehensive Windows Win32 app deployment analysis with evidence-backed outcomes, coverage details, and actionable findings.
    • Added support for deployment phases, dependencies, requirements, installer results, retries, reporting failures, and incomplete evidence.
    • Added bounded IME log parsing to prevent excessive input from overwhelming analysis.
    • Added privacy-safe export projections and broader artifact coverage.
    • Download statistics now combine extracted and synthesized content data in timestamp order.
  • Bug Fixes

    • Improved masking of legacy profile paths, doubled separators, and token-prefixed account and host values.
    • Improved detection of download starts and failure states.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 35c273ae-aa54-4e0f-833d-d0a15c5b5f4a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds a public Win32 Intune analysis API with source classification, record rules, transaction models, evidence-backed findings, redacted exports, bounded parsing, fixture validation, shared redaction updates, and per-content download synthesis.

Changes

Win32 Intune analysis

Layer / File(s) Summary
Analysis contracts and artifact sources
crates/cmtraceopen-parser/src/intune/apps/windows/win32/models.rs, crates/cmtraceopen-parser/src/intune/apps/windows/win32/sources.rs
Defines Win32 lifecycle, signal, outcome, transaction, coverage, analysis, and source-input models.
Record classification and bounded parsing
crates/cmtraceopen-parser/src/intune/apps/windows/win32/rules.rs, crates/cmtraceopen-parser/src/intune/download_stats.rs, crates/cmtraceopen-parser/src/intune/guid_registry.rs, crates/cmtraceopen-parser/src/intune/event_tracker.rs, crates/cmtraceopen-parser/src/intune/ime_parser.rs
Classifies Win32 records, centralizes GUID and download vocabulary, and limits IME parser output.
Findings, redaction, and public API
crates/cmtraceopen-parser/src/intune/apps/windows/win32/{mod.rs,findings.rs,redaction.rs}, crates/cmtraceopen-parser/src/intune/apps/windows/common/redaction.rs
Exposes the Win32 API, derives evidence-backed findings, and preserves valid redaction tokens while masking sensitive tails and legacy profile paths.
Scenario fixtures and contract validation
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/*, crates/cmtraceopen-parser/tests/intune_windows_win32.rs, crates/cmtraceopen-parser/tests/support/mod.rs, crates/cmtraceopen-parser/tests/intune_skeleton_contract.rs
Adds deployment scenario fixtures and validates findings, coverage, serialization, privacy, evidence references, and deterministic output.

Download statistics synthesis

Layer / File(s) Summary
Per-content download merging
src-tauri/src/commands/intune.rs
Merges synthesized downloads only for content IDs missing from extracted statistics and tests partial, empty, and chronological results.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ArtifactBundle
  participant Win32SourceInput
  participant classify_record
  participant analyze_win32_bundle
  participant derive_findings
  ArtifactBundle->>Win32SourceInput: captured and unreadable artifacts
  Win32SourceInput->>classify_record: confirmed source components
  classify_record->>analyze_win32_bundle: classified observations
  analyze_win32_bundle->>derive_findings: immutable Win32Analysis
  derive_findings->>ArtifactBundle: findings with evidence references
Loading

Possibly related issues

  • adamgell/cmtraceopen#357: The PR implements the Win32 deployment transaction model and fixture matrix described by the issue.

Possibly related PRs

Suggested labels: test, windows

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title follows Conventional Commits and accurately summarizes the Intune Win32 deployment transaction changes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch lane/intune-357-win32

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added apps App management related enhancement New feature or request feature New feature intune Microsoft Intune related parser Log parser related labels Aug 8, 2026
@adamgell

adamgell commented Aug 8, 2026

Copy link
Copy Markdown
Owner Author

Consolidated multi-agent review (3 dimensions, findings adversarially verified)

Verdict: needs a semantic-hardening round before merge. This lane was implemented before the Reducer Framework v1 pilot landed; it shares several of the defect classes the Store pilot just fixed, plus privacy gaps of its own. Typed-intent authority, timestamp-proximity isolation, framing delegation, and doc quality are genuinely strong — the issues below are concentrated in terminal precedence, coverage honesty, and redaction.

Confirmed Major — semantics (ADR-001/002/003)

  1. No session/attempt boundary in Win32TransactionKey + max-rank fold → unrelated later success overwrites proven failure. models.rs:217-221, reducer.rs:517-542, outcome_rank/Fold::set at reducer.rs:90-105/649-653. Two check-ins in one rotation-spanning bundle (Monday 1603, Friday 0) reduce to a single Succeeded with no finding for the failure. ADR-003 requires explicit retry linkage. Verified: set uses >= max-fold; rotation-spanning is the module's documented use case.
  2. ReportingFailed (rank 9) is absorbing; ReportSubmitted never calls set. reducer.rs:837. A transient upload failure followed by a successful upload still reports ReportingFailed with local_outcome: None when the failure precedes the local outcome — the finding text then claims no local outcome was evidenced for a device that reported fine. Also RetryScheduled (rank 6) masks subsequent ContentDeliveryFailed (rank 4).
  3. Coverage gaps do not stay gaps. reducer.rs:391, 548-567, sources.rs:268-281. A zero-byte or content-misclassified (Unknown) AppWorkload.log seeds seen_basenames by filename, suppresses push_missing_artifacts, keeps degraded_coverage false, and leaves base confidence High. ADR-001: coverage gaps cannot raise confidence.
  4. Caller input order is the timestamp tie-break, and one offset-less record degrades the whole transaction to input order. reducer.rs:723-751 (artifact_index in the sort key, all-or-nothing all_trustworthy). Same-timestamp exit code: 0 vs Not Detected flips Succeeded/InstalledNotDetected by artifact order. Zero permutation/duplication tests exist (verified).
  5. reconcile_partial_keys infers key components from bundle-wide uniqueness, and confidence_for then promotes to High on the inferred key. reducer.rs:302-343, 912-921 (verified). Enriching the bundle with a second deployment type changes grouping and terminal state; inference promotes evidence strength contrary to ADR-001.

Confirmed Major — privacy (ADR-004)

  1. stable_token is a new unsalted FNV-1a cross-export identifier (redaction.rs:26-33) — byte-identical across exports/tenants/machines and cheaply reversible against a candidate directory. ADR-004 explicitly forbids new stable correlation tokens until the equality-scope contract is defined. (Verified identical to common/redaction.rs — see also 8.)
  2. Findings are never redacted. redacted_export_projection covers only the analysis; derive_findings consumes the raw snapshot, and rules splice log-derived free text (e.g. failed_requirements) into summaries. assert_redaction never scans findings. Raw restricted values can reach exported findings.
  3. The redaction fork reintroduces a fixed bug and misses common secret shapes. user_path_re lacks common/redaction.rs's [\\/]{1,2} JSON-escape fix (verified side-by-side) → JSON-embedded profile paths leak. Credential rule requires a -// sigil → msiexec ... PASSWORD=hunter2 exports verbatim; unquoted values stop at first whitespace. Account-field rule leaks the second half of space-containing names. SIDs, hostnames, tenant/device GUIDs are not masked at all — and the fixture harness forbids SIDs in the corpus, so no fixture can ever catch it. Recommendation: collapse the fork onto common::redaction (as scripts does) and extend the shared module.

Confirmed Major — contract

  1. "One behavior owner" is unmet. Framing delegation via parse_ime_content is clean (no second CCM parser — good), but event_tracker, download_stats and win32 now hold three disjoint grammars for the same AppWorkload records with day-one drift (download_stats recognizes stall/retry-exhausted; win32 classifies them Unclassified). Nothing wraps, adapts, or documents deference. Issue Intune Windows: model Win32 app deployment transactions #357 names this explicitly.
  2. 4 of the 14 required outcome states have zero coverage (verified): NotTargeted, NotApplicable, EnforcementCommandFailed, InsufficientEvidence — no fixture, no integration test, no unit test; their finding rules are dead in test. The 16-scenario matrix is fully satisfied but is a different requirement.

Minor (selected)

  • finding_id omits execution_context → collisions across System/User transactions (rules.rs:70-80); id grammar also diverges from the family's static-kebab-case convention.
  • push_unknown_vocabulary cites all Unclassified records incl. unrelated PowerShell lines (rules.rs:240-269).
  • Metadata joins on app_id alone; Win32AppMetadata.deployment_type_id is never read (reducer.rs:672-675).
  • Return-code remap table has no deployment-type key despite its own doc contract (models.rs:157-166).
  • Capped artifacts can still set terminal Succeeded with only a one-notch confidence demotion (sources.rs:78-84).
  • pub mod everything + duplicate public paths, vs the family's private-mod + curated pub use; signals.rs/rules.rs naming is inverted vs siblings (rules→classification, findings→findings).
  • No input-size caps anywhere (~2× input text retained); (record_index + 1) as u32 silently truncates; Win32Analysis::Default yields invalid schema_version: 0 and no #[serde(default)] for forward compat.
  • Fixtures: no degraded-artifact (capped/accessDenied/parseFailed) scenario, no true-negative scenario, and the privacy fixture covers 3 needles of a much larger claimed surface.

Handled well (verified by reviewers)

Typed intent is regex-from-log-text only with no caller-writable path to outcome; timestamp proximity never creates correlation (identity-only grouping, same-minute-unrelated-apps pins it); fragments/rotation tails cannot terminate a deployment; duplicate evidence cannot inflate confidence (boolean-driven, BTreeSet); installer-failure-then-detected surfaces as contradiction instead of upgrade; unknown exit codes stay typed-unknown; classification is exact-stem + content confirmation with char-boundary safety; excellent doc comments and an executable module doctest.

Review produced by 3 parallel read-only reviewers (semantics/ADR, privacy/fixtures, API/coverage) with load-bearing findings re-verified against the branch. Refs #357, ADR-001..004.

adamgell added 12 commits August 8, 2026 12:21
Extracted from codex/recovery-intune-357-win32 onto origin/main. Parser,
reducer, rules, models, sources, redaction + 16 synthetic fixtures.
Verified: focused 21/21, full parser 2005/0, strict clippy -D warnings clean,
wasm32-unknown-unknown check clean, git diff --check clean. Lane-scoped only.
Pre-existing repo-wide rustfmt drift on main left untouched (out of lane scope).
- split_rotation: strip .log case-insensitively (mixed-case archives)
- reducer: key linked_artifacts by full Win32TransactionKey, not app_id alone
  (prevents cross-deployment-type corroboration leak)
- reducer: normalize seen_basenames via split_rotation and move the
  expected-artifact Missing loop before degraded_coverage so absent expected
  artifacts actually lower transaction confidence
- redaction: command_line_re preserves trailing switches (/quiet) for unquoted
  secrets and handles quoted values; redacted_export_projection now masks
  failed_requirements free text (correlation IDs stay verbatim)

Focused 21/21 green, strict clippy clean. Remaining majors: finding_id
uniqueness, mod.rs wildcard exports, additional regression tests.
…ction (#357)

redact_observation masked message and provenance path but left requirement_name
and retained-attribute values verbatim; both are log-derived free text that can
carry a path or UPN. Mask them via redact_text. App/deployment-type/dependency
ids and attribute names stay verbatim (correlation keys / schema labels).

Full parser 2007/0, strict clippy clean, wasm32 clean, diff-check clean.
The case-insensitive .log strip sliced the name by byte index
(&trimmed[..len-4]), which panics when the fourth-from-last byte is not a
char boundary (e.g. Журнал.log). Check the suffix on a lowercased copy and
slice only after confirming the trailing 4 bytes are the ASCII suffix, so the
boundary is always valid. Added regression tests: multi-byte stem, mixed-case
extension, non-.log extension.

Full parser 2009/0, strict clippy clean, wasm32 clean.
Review findings 1-2 (semantic majors) and the ordering half of finding 4.

The fold's max-rank Fold::set let any later-ranked statement absorb a
proven failure: an unlinked success overwrote a failed enforcement
cycle, ReportingFailed (rank 9) was absorbing even after the retry
upload landed, and a scheduled retry (Deferred, rank 6) masked a
ContentDeliveryFailed (rank 4) recorded after it.

Adopt the Store pilot's proven shape (ADR-003):

- terminal statements become equal-authority TerminalCandidates carrying
  their artifact id, record number, and trusted UTC; the only linkage
  that orders two candidates is the same artifact's own record numbers
  (the agent's append-ordered journal, across enforcement-cycle
  boundaries) or strictly-later trusted timestamps both records stated
  themselves. Nothing else counts; caller vector order never does.
- a candidate explicitly ordered after another supersedes it, which is
  what lets a linked retry land on its final outcome; superseded
  *failures* survive as Win32Transaction::superseded_failures plus a
  win32-superseded-failure finding instead of being erased.
- surviving contradictory candidates stay conservative as the new
  Win32Outcome::Conflicting (non-terminal, Low confidence, its own
  finding and next-evidence request) rather than an arbitrary winner.
- ReportSubmitted records clear a ReportingFailed candidate they are
  explicitly ordered after; in-flight states (Assigned, Enforcing,
  Deferred) resolve by pending_rank and never contest a terminal.
- order_records drops artifact_index (caller vector position) from
  every sort key: canonical (artifact_id, record_number) is the
  fallback, so permuting the input vector changes no conclusion.
  Per-record timestamp trust stays all-or-nothing for the phase fold
  because a total sort over a partial order needs an arbitrary
  tie-break; outcome resolution uses the partial order directly.

New adversarial tests: linked retry supersession with failure survival,
unorderable cross-artifact contradiction (both input orders), offsetless
cross-artifact contradiction, trusted strictly-later linkage (both input
orders), report-clearance, retry-then-failure, input-vector permutation,
and artifact duplication.
…dence (#357)

Review findings 3 and 5 (coverage honesty, ADR-001).

- An artifact whose content classified Unknown, or an Available capture
  with no content, no longer seeds the expected-artifact check by file
  name alone. The synthesized 'expected:...' Missing entry survives, the
  missing-artifact finding stays, coverage stays degraded, and the base
  confidence cap applies. A caller-declared unreadable artifact still
  closes the synthesized gap because its own coverage entry expresses
  the same absence with better fidelity (pinned by test).
- reconcile_partial_keys now marks a deployment type it filled in from
  bundle-wide uniqueness as inferred. The inference still completes the
  transaction key, but confidence_for only accepts a deployment type
  some record actually stated (itself or through its own execution
  block), so an inferred key component can no longer promote a
  transaction to High.
)

Review findings 6, 7, and the win32 half of 8 (ADR-004).

- win32/redaction.rs deletes its local rule copies and stable_token and
  re-exports the shared common::redact_text, exactly as scripts does.
  The fork had already reintroduced the fixed JSON-escaped-path leak
  (its user_path_re lacked the shared [\\/]{1,2} form); a pinned test
  now proves the JSON-escaped shape masks. No new token kind or token
  API is introduced (ADR-004): the shared FNV-1a token behavior is the
  accepted status quo, and the win32-only unsalted copy is gone.
- derive_findings now passes every summary through the shared redaction
  grammar at the single push() choke point. Rules splice log-derived
  free text (failed requirement names, tokens) into summaries, and the
  raw snapshot fed a completely unredacted findings surface. Titles and
  recommended checks are static. Pinned by a unit test whose
  requirement name quotes a UPN.
- the fixture harness's redaction scan now covers derived findings with
  the same redactionMustNotContain needles as the projection.
- the shared harness gains manifest-declared privacyProbes: exact
  sensitive-shaped strings a scenario deliberately plants, exempt from
  the corpus prohibitions in that scenario only, and each probe must be
  covered by a redactionMustNotContain needle or validation fails
  (adversarial test included). The corpus-wide SID and C:\Users\
  prohibitions had made the very shapes the redaction contract must
  catch untestable.
- the privacy-redaction fixture now plants a space-containing account
  name, an MSI-property secret, a Windows SID, a device name, and a
  profile path with a space, and asserts all of them are absent from
  both the projection and the findings. Prior expectations were unsafe
  because they proved only 3 needles of the claimed surface and could
  not express these shapes at all.
- common: account/host field values may now start with '[' so a
  malformed token-lookalike is masked rather than trusted; well-formed
  tokens are skipped by the already_masked guard (idempotence pinned).
Review finding 9 (contract: one behavior owner), as the minimal wrap.

- download_stats now owns the content-download vocabulary explicitly:
  its started/completed/failed/stalled regex accessors are pub(crate)
  and its module doc states that win32 owns the transaction view while
  consuming these primitives. win32/signals.rs drops its parallel
  download regexes and composes the shared ones exactly the way
  download_stats itself does (bare 'cancelled'/'aborted' gated by the
  download-shaped-line vocabulary). Day-one drift closed: stall/timeout
  phrasing is now recognized as the new Win32Signal::DownloadStalled --
  deliberately not a terminal statement, because a stall is trouble in
  flight, not a proven outcome.
- guid_registry now owns the one textual GUID shape (GUID_PATTERN);
  event_tracker's win32 guid rules and win32's identifier rules both
  compose it instead of spelling three private copies.
- event_tracker gains the same ownership note.
- primitives deliberately not reused are documented at the site:
  appworkload_retry_re conflates retry-exhausted with retry-scheduled,
  which is harmless for statistics and wrong for a transaction outcome;
  'content downloaded successfully' and 'downloading content' are IME
  wordings the shared vocabulary does not carry and are kept as local
  additions, not copies.
- ordering fix surfaced by the composition: content-unavailable is now
  checked before the shared download-failed alternation so the specific
  no-usable-content diagnosis wins over the generic delivery failure.
Review finding 10. NotTargeted, NotApplicable, EnforcementCommandFailed,
and InsufficientEvidence had no fixture, no integration test, and no
unit test; their finding rules were dead in test.

Four new corpus scenarios (the pinned SCENARIOS matrix grows 16 -> 20,
and the corpus-equality test enforces the growth):

- not-targeted: removal from targeting is a terminal answer with no
  next-evidence request and no enforcement phase claimed.
- not-applicable: terminal, distinct from not-targeted, and a failed
  applicability pass does not advance the last confirmed phase.
- enforcement-command-failed: terminal, with no installer return token
  asserted because the installer never ran; the launch error code stays
  on the record, not on the transaction.
- insufficient-evidence: identity alone never becomes a verdict; the
  unresolved finding names the smallest artifact that would advance the
  diagnosis, and no phase is claimed.
Selected minors from the consolidated review:

- finding_id now carries the full transaction key including
  execution_context, so System and User deployments of the same app and
  deployment type no longer collide; the rule segment stays static
  kebab-case and every key segment is lowercase, one grammar with the
  coverage ids. All 20 fixture expectations updated (prior ids were
  ambiguous across contexts by construction).
- push_unknown_vocabulary cites exactly the records that raised the
  flag via the new Win32Observation::enforcement_shaped_but_unmatched
  (serde-defaulted, additive) instead of dragging every unclassified
  line into the finding.
- metadata join and the return-code table both honor
  deployment_type_id: a scoped entry wins, an app-wide entry applies
  everywhere, and an entry for a different deployment type never labels
  or remaps this transaction. Win32ReturnCodeMapping gains an optional
  serde-defaulted deployment_type_id.
- Capped decision (ADR-001), documented at confidence_for and pinned by
  test: framed records in a truncated artifact are authentic evidence,
  so a terminal outcome (including Succeeded) stands, but the missing
  tail is a coverage gap and High is unreachable.
- input bound: MAX_RECORDS_PER_ARTIFACT (100k) caps per-artifact framed
  records; overflow marks the artifact Capped in coverage with a
  detail, which degrades confidence like any truncation. The
  '(record_index + 1) as u32' silent truncation becomes a checked
  conversion guarded by the bound.
- Win32Analysis::default() now yields the current schema_version
  instead of an invalid 0, and schema_version deserializes with a
  default for forward compatibility.
- new corpus-wide referential-integrity test: every cited evidence id
  (transactions, superseded failures, unkeyed list, findings) resolves
  to a real observation, and every coverage-gap citation resolves to a
  real coverage entry.
#357)

Pure rename plus encapsulation, no behavior change (2246 tests
unchanged before and after):

- signals.rs -> rules.rs (record classification) and rules.rs ->
  findings.rs (derive_findings), matching microsoft_store and the rest
  of the family where 'rules' means classification and 'findings' means
  findings.
- private modules with one curated pub use surface: every public name
  is now reachable at exactly one path (win32::Name); the previous
  pub-mod-everything layout published each item at two paths.

Reference sweep per the no-semantic-search checklist: direct imports
(reducer.rs super::signals), type-level references (none external),
string/doc references (mod.rs table and ownership note), re-exports
(mod.rs curated list), and tests (integration tests import only the
curated surface; no test named the old paths).
@adamgell
adamgell force-pushed the lane/intune-357-win32 branch from ea7ad7d to 1b2a40d Compare August 8, 2026 16:22
adamgell and others added 8 commits August 8, 2026 12:50
…linkage-based (#357)

Two ADR-003 defects in the reducer:

- resolve_outcome indexed surviving[0] on a set that a sequencing cycle can
  empty: sequenced_after mixes same-artifact record order with cross-artifact
  trusted timestamps and is not transitive, so a 3-cycle eliminated every
  candidate and panicked. An empty survivor set now reduces to Conflicting
  with no superseded entries, since supersession requires a surviving
  eliminator.

- DetectionSatisfied/DetectionNotSatisfied decided pre/post-enforcement from
  fold position, whose cross-artifact fallback half is the (artifact id,
  record number) sort -- so the caller's artifact naming picked the diagnosis.
  Detection verdicts are now placed by explicit linkage against the
  transaction's enforcement records (EnforcementLinkage): terminal claims are
  minted only with sequenced_after proof, and an unprovable position stays
  cited evidence.

Regression tests: a_sequencing_cycle_across_artifacts_stays_conservative_
instead_of_panicking, detection_diagnosis_is_decided_by_linkage_not_artifact_
id_sort_order.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ate and restore lost phrasings (#357)

- download_stats' transition-template ignore gate (Adding new state
  transition - From: ...) was private and checked first upstream, but win32
  composed the download vocabulary without it, so a template line quoting
  'Download Failed' minted a terminal ContentDeliveryFailed. The gate is now
  exposed as is_state_transition_template and checked before every composed
  download check in win32, mirroring the owner's own composition.

- The consolidation into download_stats dropped pre-refactor IME phrasings.
  The owner regexes now carry them so both consumers gain the recall:
  failed: 'Download has failed', 'Download state: Failed',
  'Download result = Failed'; completed: 'Download is complete',
  'Download is completed', 'Download complete'; started:
  'Started the download', 'Start content download'.

Tests: the_state_transition_template_never_mints_a_download_signal,
every_pre_refactor_download_phrasing_still_classifies (win32), and
the_shared_vocabulary_carries_the_pre_consolidation_ime_phrasings
(download_stats owner pin).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ounded framing (#357)

- A readable artifact whose records failed content confirmation reported
  coverage Available while being excluded from reduction. It now downgrades
  to ParseFailed in the one coverage entry, so degraded_coverage, the
  win32-coverage-unusable-artifact finding, and confidence all see the same
  answer (test: a_content_misclassified_artifact_is_not_reported_available).

- installer_artifacts was keyed by lowercased basename, so a second input
  with the same basename silently overwrote the first: one artifact vanished
  and the survivor corroborated on a guess. The map now keeps every artifact
  id per basename; a reference links only when exactly one candidate exists,
  and ambiguous candidates all stay visible in unlinked_installer_artifacts,
  sorted for ADR-003 canonicality (test:
  same_basename_installer_artifacts_never_collapse_or_link_ambiguously).

- MAX_RECORDS_PER_ARTIFACT was enforced by parsing everything and truncating
  afterwards, so the cap bounded state but not framing work, and the Capped
  coverage detail ('records past the bound were not read') was false. Framing
  now stops at the bound: parse_ime_content_bounded frames one record past
  the limit to detect the remainder, and the record, fragment, and fallback
  paths all honor it (tests:
  bounded_parsing_stops_at_the_limit_and_reports_the_remainder,
  bounded_parsing_bounds_the_fallback_and_fragment_paths_too).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nd generalize retry clearing (#357)

- return_code / return_code_kind / reboot_required were the fold's last
  write, so a retry-kind 1618 completion clobbered the outcome-bearing
  cycle's attribution: 1603-then-1618 exported (1618, Retry) and silenced
  the unmapped-code finding, and 3010-then-1618 paired a Retry token with a
  stale reboot flag. The exported triple now comes from the winning
  completion candidate (winning_attribution): retry-kind completions mint no
  candidate and can never clobber it, and a Conflicting outcome still drops
  the attribution entirely.

- ReportSubmitted-clears-ReportingFailed was a bespoke vector while a
  download completed after a download failure cleared nothing. Both now go
  through one Clearance mechanism (phase + polarity: a positive statement
  supersedes earlier candidates of one signal it is explicitly ordered
  after). DownloadCompleted clears DownloadFailed candidates only --
  hash-validation and staging failures happen after a download completes and
  are deliberately not cleared. Cleared failures survive as
  superseded_failures instead of vanishing, for reporting and download alike.

Tests: a_retry_kind_completion_does_not_clobber_the_outcome_bearing_return_
code, a_retry_kind_completion_after_a_reboot_success_keeps_the_winning_
attribution, a_download_completed_after_a_download_failure_clears_it_like_
reporting, a_download_completed_does_not_clear_a_hash_or_staging_failure.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…profile roots (#357)

- account_field_re / host_field_re re-hashed a value that began with an
  emitted token plus trailing prose ('UserName: [upn:...] is retrying'
  became one opaque [account:...] hash), destroying the stable token and the
  prose. A starts_with_token guard in both closures now preserves such
  values whole -- the equivalent of the lost leading-[ exclusion, chosen
  over the character-class form because a malformed token-lookalike must
  still be masked rather than trusted (pinned by
  malformed_mask_tokens_are_not_treated_as_already_masked). Stale doc
  comments on both rules rewritten to describe the real guard.

- The profile-path prefix lost 'Documents and Settings'; the alternation now
  covers both roots in the same [\\/]{1,2} single-or-JSON-escaped form.

Tests: an_account_value_starting_with_a_token_keeps_the_token_and_the_prose,
a_host_value_starting_with_a_token_is_not_rehashed,
a_documents_and_settings_profile_path_is_masked.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…#356/#357)

Three holes in the privacyProbes mechanism:

- Substring coverage counted as coverage: a needle matching any fragment of
  a probe exempted the whole probe from the prohibition scan while only the
  fragment was proven redacted. A probe must now be declared verbatim as a
  redactionMustNotContain needle; the win32 privacy fixture declares its
  path probe exactly.

- The probe strip ran over manifest.json and expected.json wholesale, so a
  probe leaking into an asserted output, an assertion, or a sanitized path
  was silently erased before the scan. The strip is now scoped to evidence
  files; descriptors are scanned with only the two declaration fields
  (privacyProbes, redactionMustNotContain) removed, so a leak anywhere else
  in them stays detectable. validate_descriptor_privacy drops its probes
  parameter accordingly.

- A privacyProbes value that is present but not an array (or carries
  non-string / empty entries) was silently treated as no probes; it now
  fails validation loudly.

Mutation tests: a_probe_covered_only_by_a_substring_needle_is_rejected,
a_non_array_privacy_probes_declaration_is_rejected,
a_probe_leaking_into_a_descriptor_output_field_is_detected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
)

AGENTS.md forbids compatibility fallbacks; nothing deserializes
Win32Analysis today, and the sibling StoreAnalysis carries no default. A
payload missing schemaVersion now fails to deserialize instead of silently
minting the current version.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
needless_borrow at the return-code classification call and a contains()
simplification in the fixture harness.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@adamgell

adamgell commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

Hermes charter review

Reviewed exact head e2438253216f9eb10cfedc251e0cfaf238b87bdd against origin/main, using the repository charter load order and the three layers: contract, adversarial, then mechanical. I read branch content only with git show origin/lane/intune-357-win32:PATH. Prior fix-round items are not re-reported below.

Findings (most severe first)

1. High — bundle-wide execution-context inference can merge unrelated transactions

crates/cmtraceopen-parser/src/intune/apps/windows/win32/reducer.rs:332-371 builds contexts_by_app across the entire input bundle and assigns an Unknown context from any single observed context for that app. Execution context is part of Win32TransactionKey, but it is not a stable app identity and is not proven by app ID alone. A concrete counterexample is two same-app records from separate deployments/captures: one record states in system context; another user-context transaction has an omitted/unsupported context. The latter is promoted to System, then groups with the former and can inherit its terminal outcome, return-code attribution, evidence, and confidence. This is exactly the weak-identity promotion prohibited by ADR-002 and can produce a plausible false deployment story. Context should remain unknown unless established within the record’s own execution block / explicit transaction linkage; add a regression with same app + distinct contexts + one missing context.

Disposition: Valid, merge-blocking semantic finding. Not listed in the prior fix-round tables.

2. High — a block silently assigns one deployment type across conflicting deployment types

crates/cmtraceopen-parser/src/intune/apps/windows/win32/reducer.rs:271-321 detects conflicting app IDs in an execution block, but it does not detect conflicting deployment_type_id values or conflicting execution contexts. It retains the first deployment type/context and propagates them to every record lacking those fields. A concrete input is one CCM thread/block containing a policy for app A / deployment type DT1, an explicit app A / DT2 record, and an unkeyed completion or failure. The completion/failure is assigned DT1 merely because DT1 was encountered first; its terminal evidence can then be attributed to the wrong deployment type, while the explicit DT2 evidence remains in another transaction. This violates explicit identity boundaries and can make return-code tables, metadata, and findings describe the wrong deployment type. Conflicting components should make propagation conservative, with a regression covering DT1/DT2 in one block.

Disposition: Valid, merge-blocking semantic finding. Not listed in the prior fix-round tables.

3. Medium — the new public findings API is not fully documented as a published-crate contract

The final head still exposes derive_findings as a public API at crates/cmtraceopen-parser/src/intune/apps/windows/win32/findings.rs:26-29, with only a one-line Rustdoc summary. The CodeRabbit follow-up identified the missing documentation for deterministic ordering, evidence semantics, and the fact that the input is an immutable reduced snapshot; it was outside the diff range and was not fixed on this head. This is not a reducer false-story bug, but it leaves a new published API underspecified and weakens the contract surface.

Disposition: Valid, non-blocking documentation finding. The earlier CodeRabbit request is accepted here; it was not part of the already-fixed behavioral rounds.

Contract layer

  • Evidence strength vs confidence: Mostly conforms. Terminal outcomes are candidate-based, unknown return codes remain unmapped, capped/unusable artifacts degrade coverage/confidence, and non-terminal states cannot reach High. Finding 1 is a confidence/correlation violation because inferred execution context can make unrelated evidence appear keyed.
  • Identity/correlation strength: Keyed app/deployment/context identity and no timestamp-only joins are good. The two findings above show that block/bundle inference still promotes partial identity beyond what the evidence proves.
  • Chronology and terminal precedence: The candidate/clearance model preserves contradictions, avoids caller-vector order, and keeps retry failures visible. I found no additional surviving terminal-precedence defect in the reviewed code beyond the identity contamination paths above.
  • Coverage honesty: Missing, capped, malformed, unkeyed, and unlinked evidence is surfaced. The 20-scenario fixture matrix and harness assertions cover the declared contract, but no dedicated degraded-artifact corpus scenario exists; that is explicitly deferred in the PR body and is therefore recorded, not re-reported as a new defect.
  • Redaction scope: The lane now composes the shared redaction grammar and tests the projection/findings surface. I found no new raw-sensitive-value leak in the prioritized production paths. ADR-004 token equality scope remains explicitly provisional, as the PR body states.

Adversarial layer

Applied the charter attack surface to identity and transaction boundaries: same app across contexts, same app across deployment types, partial keys, out-of-order and cross-artifact evidence, duplicate/irrelevant records, retry/terminal contradictions, malformed/capped evidence, supplemental artifact ambiguity, and redaction. The two high findings are concrete false-story attacks that survive the existing tests. The existing permutation, duplicate-id, cycle, detection-linkage, retry, template, and privacy tests were treated as evidence of coverage rather than repeated as findings.

Mechanical layer

  • No untrusted-input expect remains in the two new inference paths; the checked bounded record conversion is guarded by the parser cap. Regex compilation expects are static initialization assertions.
  • The branch body reports Rust tests, Clippy, workspace check, wasm check, TypeScript, and diff checks clean at the round-4 head; I did not treat those claims as current gate evidence without matching GitHub results.
  • Current GitHub checks for this exact head: most completed checks pass, but Check & Test (Rust) and ESP Diagnostics (Windows) are still in_progress at review time. No exact-head green CI gate yet.

Gate states

  • CI: PENDING — current exact-head checks are not all complete; Rust and Windows ESP jobs remain in progress.
  • CodeRabbit approved_at_head: NOT SATISFIED — the current CodeRabbit check says “Review skipped: draft pull request”; the latest CodeRabbit review state is COMMENTED, not an approval at e2438253.
  • Contract conformance: NOT SATISFIED — the workload fixture/unit contract is extensive, but the two identity-inference counterexamples above violate ADR-002/ADR-003 conformance. There is no generic reducer-conformance gate on this lane that disproves them.

Explicitly rejected / not re-reported

  • I did not re-report the 17 CodeRabbit fix-round items that the PR body maps to commits, or the one explicitly declined glob re-export item; the final code reflects those dispositions.
  • I did not treat CodeRabbit’s passing status check as review approval because GitHub explicitly reports that review was skipped for the draft.
  • I did not re-report the documented deferrals for ADR-004 token equality scope, corpus-level degraded/true-negative scenarios, or per-record timestamp trust in the phase fold; they remain coverage/deferred work as stated by the author.

Coverage statement

This review covered the charter, routing indexes, specialist context, reducer ADRs/checklist, contract/adversary/integration charters, the full production Win32 modules, prioritized shared-file changes, the Win32 harness/support changes, PR fix-round history, exact-head GitHub checks, and CodeRabbit state. It did not claim native Intune/Windows acceptance, did not check out the branch, did not modify repository files, and did not resolve threads or merge anything.

Review disposition: two merge-blocking semantic findings remain; gate state is reported above rather than issuing a generic merge verdict.

adamgell and others added 4 commits August 8, 2026 22:26
…s observed context

Hermes charter review on PR #525, finding 1 (High). reconcile_partial_keys
widened an Unknown execution context to the single context observed anywhere
in the bundle for that app. Execution context is a runtime session property,
not app identity: the same app deploys in System and in User context as
distinct transactions, so the promotion merged unrelated captures and let one
inherit the other's terminal outcome, return-code attribution, evidence, and
confidence (ADR-002 weak-identity promotion).

A context now stays Unknown unless the record stated it or its own execution
block established it, and an unknown context keys its own transaction.

Deployment-type uniqueness inference in the same function is retained
deliberately: a deployment type id is a stable configuration identity
subordinate to an exactly-matched app id (moderate strength under ADR-002),
it is flagged deployment_type_inferred, and it never raises confidence
(ADR-001). The doc comment now states that boundary explicitly.

RED counterexample (Hermes's): same app, one System-context success capture,
one context-less failure from a separate capture. Before the fix they fused
into one System transaction whose outcome the unrelated failure overwrote.

The privacy-redaction fixture relied on the promotion: its IME policy record
states no context and sits in no shared execution block with the AppWorkload
records, so it now honestly keys its own unknown-context transaction. The
expectation records the split and the new unresolved finding.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… dispute

Hermes charter review on PR #525, finding 2 (High). apply_block_key refused
to spread a key only when two records named different apps; a block stating
two deployment types or two execution contexts silently kept the first value
seen and spread it to every record lacking that field, attributing unkeyed
terminal evidence to whichever deployment type or context happened to be
logged first (ADR-002 explicit identity boundaries).

The conflict rule is now per component, mirroring the app-id rule: a block
whose records state two apps still refuses to key at all (the block boundary
itself is wrong); a block whose records dispute the deployment type or the
execution context withholds that component, so an unkeyed record keeps a
partial key instead of a guess while uncontested components still spread.

RED counterexamples (Hermes's): one block with a DT1 policy, an explicit DT2
record, and an unkeyed completion - the completion adopted DT1; and the same
shape with System/User contexts - the completion adopted System. Both now
key partially and carry their own outcome.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hermes charter review on PR #525, finding 3 (Medium), accepting the earlier
CodeRabbit follow-up that was outside the diff range: derive_findings is a
public API of the published cmtraceopen-parser crate and carried only a
one-line summary. The Rustdoc now specifies the contract: the input is an
immutable reduced snapshot (no live state, no I/O, same snapshot in, same
findings out), the returned ordering is deterministic (fixed rule order, then
the snapshot's canonical key-sorted transaction order, stable finding ids),
and the evidence semantics (every finding is evidence-backed against the
snapshot, independent severity/confidence axes, summaries pass the shared
redaction grammar). No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CONFIG CHANGE, pre-authorized with mandatory disclosure: this lane branched
before main's 96b841c, so its .coderabbit.yaml still carried drafts: false,
request_changes_workflow: false, and auto_apply_labels: false. CodeRabbit
reads the config from the PR branch, so on this draft PR it reported 'Review
skipped: draft pull request', which blocks incremental reviews and the
approve flow the merge gate requires.

This commit copies origin/main's .coderabbit.yaml byte-for-byte (zero diff
against main) and changes nothing else.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@adamgell

adamgell commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

Re: Hermes charter review — dispositions (fixed at 35aadf72)

All findings addressed on this head, TDD with the review's exact counterexamples (each regression confirmed RED against e2438253 before the fix), one commit per finding.

Finding 1 (High) — bundle-wide execution-context inference: accepted, fixed in 9da4acc4

reconcile_partial_keys no longer builds contexts_by_app or promotes an Unknown context to the app's single observed context. A context is set only by the record itself or by its own execution block (apply_block_key), and an unknown context keys its own transaction.

  • RED regression: a_context_less_record_never_adopts_the_bundles_observed_context — same app, one System-context success capture, one context-less failure from a separate capture. Before the fix: one merged System transaction whose outcome the unrelated failure overwrote (exactly the reviewed false story). After: two transactions, each keeping its own outcome, attribution, evidence, and confidence.
  • The same-function deployment-type inference was audited against the same discipline and deliberately retained: a deployment type id is a stable configuration identity subordinate to an exactly matched app id (moderate-strength completion under ADR-002's "stable secondary identity"), it is flagged deployment_type_inferred, and it never counts toward confidence (ADR-001). Execution context, by contrast, is a runtime session property and not identity at all. The boundary and its rationale are now stated in the function's Rustdoc.
  • Fixture fallout: privacy-redaction relied on the promotion (its IME policy record states no context and shares no execution block with the AppWorkload records). Its expectation now records the honest split: a user-context succeeded transaction plus an unknown-context assigned transaction with a win32-unresolved finding.

Finding 2 (High) — block-key propagation past a conflicting component: accepted, fixed in b17e959d

apply_block_key now applies the conflict rule per key component, mirroring the app-id rule: two apps in one block still refuse to key at all (the block boundary itself is wrong); a block whose records dispute the deployment type or the execution context withholds that component, so unkeyed records keep a partial key rather than adopting whichever value was logged first. Uncontested components still spread.

  • RED regressions: a_block_with_conflicting_deployment_types_refuses_to_spread_one (DT1 policy + explicit DT2 record + unkeyed completion — the completion no longer adopts DT1; it keys (app, None, unknown) and carries its own failure while the DT1 transaction stays Assigned) and a_block_with_conflicting_execution_contexts_refuses_to_spread_one (System + User in one block — the unkeyed completion stays context-Unknown; the uncontested deployment type still spreads).

Finding 3 (Medium) — derive_findings published-crate contract: accepted, fixed in 79a27e82

Rustdoc now documents the input as an immutable reduced snapshot (no I/O, no live state, same snapshot in, same findings out), the deterministic ordering (fixed rule order, then the snapshot's canonical key-sorted transaction order, stable finding ids), and the evidence semantics (every finding evidence-backed against the snapshot, independent severity/confidence axes, summaries pass the shared redaction grammar). No behavior change.

Gate state noted by the review — CodeRabbit skip: config synced in 35aadf72

Disclosure: this lane's .coderabbit.yaml predated main's 96b841cc (drafts: false), which is why the check said "Review skipped: draft pull request". 35aadf72 copies main's .coderabbit.yaml byte-for-byte (zero diff vs main) and changes nothing else, so incremental reviews and the approve flow can run. Also stated prominently in the PR body.

Verification (after 35aadf72)

  • cargo test --locked -p cmtraceopen-parser --no-fail-fast: 49 suites, 2293 passed, 0 failures
  • cargo clippy --locked -p cmtraceopen-parser --all-targets -- -D warnings: clean
  • cargo clippy --workspace --all-targets -- -D warnings: clean
  • cargo check --workspace: clean
  • cargo check -p cmtraceopen-parser --target wasm32-unknown-unknown: clean

@adamgell

adamgell commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot added test Testing related windows Windows platform related labels Aug 9, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/cmtraceopen-parser/tests/support/mod.rs (1)

879-887: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Exclude trailing punctuation from the returned email occurrence.

Line 879 trims terminal punctuation from domain, but Line 887 stores the untrimmed token. If content contains user@contoso.com., a declared user@contoso.com probe does not cover the returned span. The validator then reports a false email leak.

Build the occurrence from local and the trimmed domain. Add a regression for an email followed by a period.

Proposed fix
-            occurrences.push((start, token.to_owned()));
+            occurrences.push((start, format!("{local}@{domain}")));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cmtraceopen-parser/tests/support/mod.rs` around lines 879 - 887,
Update the occurrence construction in the email-scanning logic to use the
trimmed domain together with local, rather than storing the original token with
trailing punctuation. Preserve the existing domain validation and invalid-domain
exclusions, and add a regression test covering an email followed by a period.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/cmtraceopen-parser/tests/support/mod.rs`:
- Around line 879-887: Update the occurrence construction in the email-scanning
logic to use the trimmed domain together with local, rather than storing the
original token with trailing punctuation. Preserve the existing domain
validation and invalid-domain exclusions, and add a regression test covering an
email followed by a period.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 546c77a9-9d86-4c7f-a42d-a526f0f624fd

📥 Commits

Reviewing files that changed from the base of the PR and between dc66cac and 35aadf7.

📒 Files selected for processing (6)
  • .coderabbit.yaml
  • crates/cmtraceopen-parser/src/intune/apps/windows/win32/findings.rs
  • crates/cmtraceopen-parser/src/intune/apps/windows/win32/reducer.rs
  • crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/privacy-redaction/expected.json
  • crates/cmtraceopen-parser/tests/intune_windows_win32.rs
  • crates/cmtraceopen-parser/tests/support/mod.rs

@adamgell

adamgell commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

Hermes charter re-review (head 35aadf7)

Reviewed the branch at exact head 35aadf72459e4a10ec239e6a0b7b04e19ace5c9e using git show origin/lane/intune-357-win32:PATH; no checkout or repository changes were made.

Prior findings

  1. High — bundle-wide execution-context promotion: CLOSED (9da4acc4)

    reconcile_partial_keys no longer infers execution context from the bundle. A context-less record remains Unknown unless its own record/block states the context, and the unknown context participates in its own transaction key. The added regression a_context_less_record_never_adopts_the_bundles_observed_context exercises a System success plus a separate context-less failure and verifies that the failure does not overwrite the System transaction. This removes the false cross-session merge and inherited outcome/attribution/confidence that blocked the prior head.

  2. High — block-key conflict detection was app-only: CLOSED (b17e959d)

    apply_block_key now tracks conflicts independently for app id, deployment type id, and execution context. A disputed component is withheld from unkeyed records rather than first-wins propagated; uncontested components may still spread. The added deployment-type and execution-context conflict regressions verify partial keys and preserve each record's own evidence/outcome. This closes the prior path by which an unkeyed terminal record could be attributed to whichever DT/context appeared first.

  3. Medium — derive_findings published API documentation: CLOSED (79a27e82)

    The public function now documents its immutable snapshot/no-I/O input contract, deterministic rule and transaction ordering, stable finding-id scope, evidence/coverage backing, independent severity/confidence axes, and redaction behavior. This addresses the documentation gap without changing behavior.

DT-inference adjudication (ADR-002)

I accept the distinction; I do not treat deployment-type inference as the same defect. ADR-002 expressly permits a stable secondary identity at moderate strength. Here the retained inference is narrowly bounded: it requires an exactly matched app identity, exactly one observed deployment type for that app, and it is subordinate to the app key. Ambiguous apps retain a partial key. The inferred component is explicitly marked deployment_type_inferred and is excluded from the observed-deployment-type confidence input, so it cannot promote confidence to High. That is materially different from execution context, which is runtime session state: the same app can legitimately run in System and User contexts, so bundle-wide uniqueness cannot establish which session an uncontextualized record belongs to.

This acceptance is conditional on the implementation boundary shown at this head: DT inference must remain an identity completion only, never a standalone join or confidence upgrade. The current code and regression an_inferred_deployment_type_cannot_promote_confidence_to_high satisfy that boundary.

Privacy-redaction fixture

Accepted as honest. The changed expectation records two transactions: the context-bearing User transaction remains successful, while the IME policy record that states no context and is outside the AppWorkload execution block becomes a separate Unknown-context Assigned transaction with low confidence and an unresolved finding. The prior single-transaction expectation depended on the now-invalid context promotion; retaining it would preserve the false story. The fixture still asserts the redaction probes and preserves app/DT ids, so the split does not weaken the privacy checks.

Gate states observed at review time

  • Hermes charter review: this comment is the exact-head re-review; all three prior findings are verified closed and no blocking finding remains.
  • CodeRabbit: SUCCESS status at head; latest CodeRabbit review is APPROVED at 35aadf72 (approved_at_head).
  • CI: not fully settled at observation time. TypeScript Check, E2E, MSRV Windows, CodeQL, and CodeQL language analyses were successful; Rust Check & Test (Rust) and ESP Diagnostics (Windows) were still IN_PROGRESS.
  • Contract conformance: the reviewed changes conform to ADR-002's explicit identity/correlation strengths and the reducer checklist's no-weak-promotion/no-false-correlation requirements. This is a code/fixture review, not native Windows validation.

Verdict

My two blocking identity-inference findings are cleared at head 35aadf72, and the derive_findings documentation finding is also closed. No Hermes blocking finding remains. Merge readiness still depends on the in-progress CI jobs completing successfully; this comment does not claim native/lab validation.

@adamgell

adamgell commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai approve

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
✅ Action performed

Comments resolved and changes approved.

@adamgell

adamgell commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@adamgell
adamgell marked this pull request as ready for review August 9, 2026 16:51
Copilot AI lite review requested due to automatic review settings August 9, 2026 16:51
@adamgell

adamgell commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Intune Win32 deployment transaction analyzer to cmtraceopen-parser (evidence-backed, phase-aware, privacy-safe exports), plus supporting fixture/harness updates and a small src-tauri download-stats merge fix so Intune diagnostics remain stable in the UI.

Changes:

  • Implement cmtraceopen_parser::intune::apps::windows::win32 (models, signal classification, reduction, findings, and redaction).
  • Extend the fixture harness with explicit privacyProbes support and stronger privacy scanning semantics (evidence + descriptors).
  • Improve Intune downloads handling in src-tauri by merging synthesized download stats per content-id and sorting merged downloads chronologically (epoch-first).

Reviewed changes

Copilot reviewed 56 out of 112 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
.coderabbit.yaml Enables CodeRabbit review behavior on drafts and auto label / request-changes workflow settings.
src-tauri/src/commands/intune.rs Merges event-synthesized downloads per content id and adds regression tests + chronological sort.
crates/cmtraceopen-parser/src/intune/ime_parser.rs Adds bounded IME parsing (parse_ime_content_bounded) and threads entry caps through record/fragments/fallback paths.
crates/cmtraceopen-parser/src/intune/guid_registry.rs Introduces shared GUID_PATTERN for consistent GUID extraction across analyzers.
crates/cmtraceopen-parser/src/intune/event_tracker.rs Switches Win32 GUID extraction regexes to compose from GUID_PATTERN and documents behavior ownership boundaries.
crates/cmtraceopen-parser/src/intune/download_stats.rs Centralizes download vocabulary + template gate, exposes shared predicates for Win32 analyzer composition, and adds tests.
crates/cmtraceopen-parser/src/intune/apps/windows/win32/mod.rs Defines the public Win32 analyzer surface + detailed module contract docs and curated exports.
crates/cmtraceopen-parser/src/intune/apps/windows/win32/redaction.rs Implements Win32 analysis redaction/export projection using the shared redaction grammar; adds projection-focused tests.
crates/cmtraceopen-parser/src/intune/apps/windows/win32/models.rs Win32 analyzer public models (schema surface) used by fixtures and exports.
crates/cmtraceopen-parser/src/intune/apps/windows/win32/sources.rs Win32 artifact/source classification and coverage logic.
crates/cmtraceopen-parser/src/intune/apps/windows/win32/rules.rs Record classification rules for Win32 signals and tokens.
crates/cmtraceopen-parser/src/intune/apps/windows/win32/reducer.rs Identity-keyed reduction into immutable transaction snapshots and coverage handling.
crates/cmtraceopen-parser/src/intune/apps/windows/win32/findings.rs Evidence-backed findings derivation for Win32 transactions.
crates/cmtraceopen-parser/tests/intune_windows_win32.rs Integration tests validating the Win32 fixture corpus + privacy probe behavior.
crates/cmtraceopen-parser/tests/support/mod.rs Adds privacyProbes contract, probe-aware privacy scanning, and stronger descriptor validation.
crates/cmtraceopen-parser/tests/intune_skeleton_contract.rs Updates descriptor-privacy test call site to new signature.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/already-installed-detection/manifest.json Win32 fixture manifest: already-installed detection scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/already-installed-detection/expected.json Win32 fixture expected output: already-installed detection scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/already-installed-detection/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: AppWorkload.log (already-installed detection).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/already-installed-detection/evidence/app-action-processor-current/current/AppActionProcessor.log Fixture evidence: AppActionProcessor.log (already-installed detection).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/already-installed-detection/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (already-installed detection).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/complete-success/manifest.json Win32 fixture manifest: complete success scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/complete-success/expected.json Win32 fixture expected output: complete success scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/complete-success/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: AppWorkload.log (complete success).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/complete-success/evidence/app-action-processor-current/current/AppActionProcessor.log Fixture evidence: AppActionProcessor.log (complete success).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/complete-success/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (complete success).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/dependency-failure/manifest.json Win32 fixture manifest: dependency failure scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/dependency-failure/expected.json Win32 fixture expected output: dependency failure scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/dependency-failure/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: AppWorkload.log (dependency failure).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/dependency-failure/evidence/app-action-processor-current/current/AppActionProcessor.log Fixture evidence: AppActionProcessor.log (dependency failure).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/dependency-failure/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (dependency failure).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/enforcement-command-failed/manifest.json Win32 fixture manifest: enforcement command launch failure scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/enforcement-command-failed/expected.json Win32 fixture expected output: enforcement command launch failure scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/enforcement-command-failed/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: AppWorkload.log (enforcement command failed).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/enforcement-command-failed/evidence/app-action-processor-current/current/AppActionProcessor.log Fixture evidence: AppActionProcessor.log (enforcement command failed).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/enforcement-command-failed/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (enforcement command failed).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/hash-or-staging-failure/manifest.json Win32 fixture manifest: hash/staging failure scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/hash-or-staging-failure/expected.json Win32 fixture expected output: hash/staging failure scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/hash-or-staging-failure/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: AppWorkload.log (hash/staging failure).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/hash-or-staging-failure/evidence/app-action-processor-current/current/AppActionProcessor.log Fixture evidence: AppActionProcessor.log (hash/staging failure).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/hash-or-staging-failure/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (hash/staging failure).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/incomplete-bundle-missing-appworkload/manifest.json Win32 fixture manifest: incomplete bundle missing AppWorkload scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/incomplete-bundle-missing-appworkload/expected.json Win32 fixture expected output: incomplete bundle missing AppWorkload scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/incomplete-bundle-missing-appworkload/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (incomplete bundle missing AppWorkload).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/insufficient-evidence/manifest.json Win32 fixture manifest: insufficient evidence scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/insufficient-evidence/expected.json Win32 fixture expected output: insufficient evidence scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/insufficient-evidence/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: AppWorkload.log (insufficient evidence).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/insufficient-evidence/evidence/app-action-processor-current/current/AppActionProcessor.log Fixture evidence: AppActionProcessor.log (insufficient evidence).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/insufficient-evidence/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (insufficient evidence).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/installer-known-nonzero-code/manifest.json Win32 fixture manifest: mapped nonzero return code scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/installer-known-nonzero-code/expected.json Win32 fixture expected output: mapped nonzero return code scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/installer-known-nonzero-code/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: AppWorkload.log (mapped nonzero return code).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/installer-known-nonzero-code/evidence/app-action-processor-current/current/AppActionProcessor.log Fixture evidence: AppActionProcessor.log (mapped nonzero return code).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/installer-known-nonzero-code/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (mapped nonzero return code).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/installer-unknown-code/manifest.json Win32 fixture manifest: unmapped installer return code scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/installer-unknown-code/expected.json Win32 fixture expected output: unmapped installer return code scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/installer-unknown-code/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: AppWorkload.log (unmapped return code).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/installer-unknown-code/evidence/app-action-processor-current/current/AppActionProcessor.log Fixture evidence: AppActionProcessor.log (unmapped return code).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/installer-unknown-code/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (unmapped return code).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/installed-but-not-detected/manifest.json Win32 fixture manifest: installed-but-not-detected scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/installed-but-not-detected/expected.json Win32 fixture expected output: installed-but-not-detected scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/installed-but-not-detected/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: AppWorkload.log (installed-but-not-detected).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/installed-but-not-detected/evidence/app-action-processor-current/current/AppActionProcessor.log Fixture evidence: AppActionProcessor.log (installed-but-not-detected).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/installed-but-not-detected/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (installed-but-not-detected).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/no-usable-content/manifest.json Win32 fixture manifest: no usable content scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/no-usable-content/expected.json Win32 fixture expected output: no usable content scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/no-usable-content/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: AppWorkload.log (no usable content).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/no-usable-content/evidence/app-action-processor-current/current/AppActionProcessor.log Fixture evidence: AppActionProcessor.log (no usable content).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/no-usable-content/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (no usable content).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/not-applicable/manifest.json Win32 fixture manifest: not applicable scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/not-applicable/expected.json Win32 fixture expected output: not applicable scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/not-applicable/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: AppWorkload.log (not applicable).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/not-applicable/evidence/app-action-processor-current/current/AppActionProcessor.log Fixture evidence: AppActionProcessor.log (not applicable).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/not-applicable/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (not applicable).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/not-targeted/manifest.json Win32 fixture manifest: not targeted scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/not-targeted/expected.json Win32 fixture expected output: not targeted scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/not-targeted/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: AppWorkload.log (not targeted).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/not-targeted/evidence/app-action-processor-current/current/AppActionProcessor.log Fixture evidence: AppActionProcessor.log (not targeted).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/not-targeted/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (not targeted).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/privacy-redaction/manifest.json Win32 fixture manifest: privacy/redaction scenario (declares privacyProbes).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/privacy-redaction/expected.json Win32 fixture expected output: privacy/redaction scenario (redaction needles).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/privacy-redaction/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: AppWorkload.log (privacy/redaction).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/privacy-redaction/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (privacy/redaction).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/reporting-failure-after-local-outcome/manifest.json Win32 fixture manifest: reporting failure after local success scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/reporting-failure-after-local-outcome/expected.json Win32 fixture expected output: reporting failure after local success scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/reporting-failure-after-local-outcome/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: AppWorkload.log (reporting failure).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/reporting-failure-after-local-outcome/evidence/app-action-processor-current/current/AppActionProcessor.log Fixture evidence: AppActionProcessor.log (reporting failure).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/reporting-failure-after-local-outcome/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (reporting failure).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/requirement-failure/manifest.json Win32 fixture manifest: requirement failure scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/requirement-failure/expected.json Win32 fixture expected output: requirement failure scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/requirement-failure/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: AppWorkload.log (requirement failure).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/requirement-failure/evidence/app-action-processor-current/current/AppActionProcessor.log Fixture evidence: AppActionProcessor.log (requirement failure).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/requirement-failure/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (requirement failure).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/retry-without-terminal-outcome/manifest.json Win32 fixture manifest: retry scheduled / no terminal outcome scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/retry-without-terminal-outcome/expected.json Win32 fixture expected output: retry scheduled / no terminal outcome scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/retry-without-terminal-outcome/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: AppWorkload.log (retry scheduled).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/retry-without-terminal-outcome/evidence/app-action-processor-current/current/AppActionProcessor.log Fixture evidence: AppActionProcessor.log (retry scheduled).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/retry-without-terminal-outcome/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (retry scheduled).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/rotation-split-record/manifest.json Win32 fixture manifest: rotation-split record/fragment scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/rotation-split-record/expected.json Win32 fixture expected output: rotation-split record/fragment scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/rotation-split-record/evidence/app-workload-rotation/rotated/AppWorkload-1.log Fixture evidence: rotated AppWorkload segment with truncated record.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/rotation-split-record/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: current AppWorkload segment with fragment continuation.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/rotation-split-record/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (rotation-split record).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/same-minute-unrelated-apps/manifest.json Win32 fixture manifest: disjoint apps within same minute scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/same-minute-unrelated-apps/expected.json Win32 fixture expected output: disjoint apps within same minute scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/same-minute-unrelated-apps/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: AppWorkload.log (same-minute unrelated apps).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/same-minute-unrelated-apps/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (same-minute unrelated apps).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/unkeyed-malformed-record/manifest.json Win32 fixture manifest: unkeyed/malformed record + fragment scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/unkeyed-malformed-record/expected.json Win32 fixture expected output: unkeyed/malformed record + fragment scenario.
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/unkeyed-malformed-record/evidence/app-workload-current/current/AppWorkload.log Fixture evidence: AppWorkload.log (unkeyed/malformed record).
crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/win32/unkeyed-malformed-record/evidence/ime-current/current/IntuneManagementExtension.log Fixture evidence: IME log (unkeyed/malformed record).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +1175 to +1180
downloads.sort_by(|left, right| {
left.timestamp_epoch
.cmp(&right.timestamp_epoch)
.then_with(|| left.timestamp.cmp(&right.timestamp))
.then_with(|| left.content_id.cmp(&right.content_id))
});

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 56 out of 112 changed files in this pull request and generated no new comments.

Suppressed comments (3)

crates/cmtraceopen-parser/tests/intune_windows_win32.rs:412

  • The golden corpus only checks that expected findings are present; it never rejects unexpected findings. A regression that emits a false warning or failure in every scenario would still pass this suite. Compare the complete sorted finding-ID lists before validating each expected finding's fields.
    src-tauri/src/commands/intune.rs:1165
  • This deduplication is case-sensitive even though GUID identity is not. event_tracker::extract_guid preserves source casing, while explicit JSON identities in download_stats are lowercased by guid_registry; an uppercase AppId can therefore add a synthesized uppercase stat beside the extracted lowercase stat. Canonicalize both sets of content IDs before comparison.
    crates/cmtraceopen-parser/src/intune/apps/windows/win32/rules.rs:236
  • Unquoted installer-output paths with spaces cannot link. The privacy fixture itself contains Install output file: C:\Users\Probe User\...\contoso-setup.log; this branch captures only C:\Users\Probe, so file_name_from_reference produces Probe and a supplied contoso-setup.log artifact remains unlinked even though the keyed record names it. Bound the unquoted alternative by the supported artifact extension rather than whitespace.
    r#"(?i)\b(?:output|log)\s+file\s*[:=]\s*(?P<path>"[^"\r\n]+"|[^\s,;]+)"#

@adamgell
adamgell merged commit 3508879 into main Aug 10, 2026
18 checks passed
@adamgell
adamgell deleted the lane/intune-357-win32 branch August 10, 2026 01:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

apps App management related enhancement New feature or request feature New feature intune Microsoft Intune related parser Log parser related test Testing related windows Windows platform related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Intune Windows: model Win32 app deployment transactions

2 participants