Skip to content

feat(sccm): analyze client policy transactions - #391

Closed
adamgell wants to merge 36 commits into
codex/parser-family-skeletonfrom
codex/sccm-321-policy-analysis
Closed

feat(sccm): analyze client policy transactions#391
adamgell wants to merge 36 commits into
codex/parser-family-skeletonfrom
codex/sccm-321-policy-analysis

Conversation

@adamgell

@adamgell adamgell commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Implements the production reducer slice of #321 against the already-merged 14-scenario synthetic policy corpus.

Current TDD state: RED by design at 394dc17.

  • Added the focused public behavior contract first.
  • cargo test --locked -p cmtraceopen-parser --test sccm_client_policy fails only because analyze_client_policy and SccmNormalizedBundle do not exist yet.
  • The test covers all frozen terminal/coverage scenarios, deterministic input reordering, evidence-range closure, and the client-only/no-MP-root-cause boundary.
  • No production implementation, raw parser change, native I/O, or cross-side correlation has been added in this commit.

Next commit will add the smallest pure-Rust/wasm-compatible keyed reducer and the four exact policy-state source catalog tuples identified in #321. This PR will remain draft until focused/aggregate/Clippy/wasm/format gates pass and exact-head CodeRabbit plus independent false-causality review clear.

Summary by CodeRabbit

  • New Features

    • Added SCCM client-policy analysis across policy phases, outcomes, evidence, confidence, and workflow state.
    • Added support for CIAgent, CIDownloader, StateMessage, and StatusAgent policy artifacts.
    • Added normalized SCCM artifact and evidence bundles for analysis.
    • Added partial coverage status reporting.
  • Bug Fixes

    • Improved handling of malformed, ambiguous, incomplete, conflicting, and out-of-order policy evidence.
    • Added detection of missing sources, coverage gaps, rotation issues, and recovery scenarios.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c03a3007-b56c-4fd4-bb8c-dcd4a543a364

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

Adds a public SCCM client-policy workflow analyzer. It parses and correlates policy evidence, reduces workflow transactions, reports coverage and findings, handles chronology and rotation cases, and adds catalog and integration-test coverage.

Changes

SCCM client-policy analysis

Layer / File(s) Summary
Policy contracts and public wiring
crates/cmtraceopen-parser/src/sccm/{models.rs,ingest.rs,findings.rs,catalog.rs,mod.rs}, crates/cmtraceopen-parser/src/sccm/client/mod.rs, crates/cmtraceopen-parser/src/sccm/client/policy.rs
Adds public policy-analysis types, normalized bundles, client-policy catalog entries, Partial coverage handling, and module exports.
Evidence extraction and transaction reduction
crates/cmtraceopen-parser/src/sccm/client/policy.rs
Validates evidence, parses policy markers, resolves phase outcomes, checks chronology, and reduces facts into workflow transactions.
Coverage, findings, and output normalization
crates/cmtraceopen-parser/src/sccm/client/policy.rs
Computes coverage gaps, derives repair requests, handles rotation-split records, constructs findings, and normalizes analysis output.
Client-policy contract validation
crates/cmtraceopen-parser/tests/sccm_client_policy.rs, crates/cmtraceopen-parser/tests/sccm_spine_contract.rs
Tests parsing boundaries, evidence identity, chronology, coverage, recovery, rotation handling, findings, ordering, serialization, and catalog declarations.

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

Sequence Diagram(s)

sequenceDiagram
  participant SccmNormalizedBundle
  participant analyze_client_policy
  participant PolicyFactParser
  participant PolicyTransactionReducer
  participant SccmWorkflowAnalysis
  SccmNormalizedBundle->>analyze_client_policy: provide normalized artifacts and evidence
  analyze_client_policy->>PolicyFactParser: validate and parse admissible evidence
  PolicyFactParser->>PolicyTransactionReducer: provide policy facts and provenance
  PolicyTransactionReducer->>SccmWorkflowAnalysis: construct workflow transactions
  SccmWorkflowAnalysis-->>analyze_client_policy: return analysis results
Loading

Possibly related issues

Possibly related PRs

Suggested labels: test

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 67.90% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding SCCM client policy transaction analysis.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

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

@adamgell

Copy link
Copy Markdown
Owner Author

#321 production policy reducer — exact-head implementation gate

Owner/worktree: root agent — /Users/Adam.Gell/repo/cmtraceopen/.worktrees/sccm-321-policy-analysis on codex/sccm-321-policy-analysis

Exact head: 1e5a3174ceaaf2f7d045d4e36a4e1955a8b06f91

Dependency state: #318 shared spine is merged at a8a367c6; #319 intake and the #321 synthetic corpus are present on that base. This PR does not consume #320 reducer output and does not implement #333 correlation.

Implemented scope: pure-Rust/wasm-compatible SccmNormalizedBundle plus an independent request → download → persist → schedule → evaluate → report reducer. It keeps raw CCM as the transport parser, preserves public LogEntry, accepts only the synthetic policy-client-5.00.test-v1 profile, emits cited transactions/findings/gaps/requests, and adds explicit partial SCCM coverage. Missing/partial/unknown-version/unsafe/malformed evidence remains local or incomplete. Client output makes no MP-side causal claim.

TDD evidence:

  • 394dc17c: initial focused contract RED because analyze_client_policy/SccmNormalizedBundle did not exist.
  • e24d2999: adversarial probes reproduced two fail-open behaviors RED: Status=0 substantiating a terminal failure and a normalized cross-artifact phase-time inversion still producing high success.
  • Aggregate spine then went RED at 52 declared sources vs 48 pinned tuples, forcing the four exact client-policy source cards into the public catalog contract.
  • 1e5a3174: smallest reducer/catalog/coverage implementation; all focused and aggregate gates green.

Fixture/test matrix: 14 frozen sanitized scenarios (complete, request/download/persist/evaluate/report failures, deferred, rotation split, malformed, incomplete, contradictory gates/offset, recovery, multiline) plus unknown/missing profile, unsafe handle/path, partial source, unrelated assignment, same-time distinct keys, wrong-source phase, nonzero terminal result, coherent same-key recovery, public-output privacy, deterministic input reversal, exact catalog, keys/counterpart facts, source-local citations, conservative finding validation.

Verification at exact head:

  • cargo test --locked -p cmtraceopen-parser --test sccm_client_policy — 13/13
  • neighbor intake/health — 3/3 and 3/3
  • cargo test --locked -p cmtraceopen-parser --test sccm_spine_contract — 136/136
  • cargo test --locked -p cmtraceopen-parser — 742/742
  • cargo clippy --locked -p cmtraceopen-parser --all-targets -- -D warnings — pass
  • cargo +1.88.0 check --locked -p cmtraceopen-parser --target wasm32-unknown-unknown — pass
  • npx tsc --noEmit — pass
  • 28 policy JSON fixtures via jq empty — pass
  • scoped Rustfmt, git diff --check, clean worktree — pass
  • workspace-wide cargo fmt --all -- --check still reports 17 inherited unrelated files; no owned-file overlap.

Review/acceptance state: draft/open. Local CodeRabbit 0.7.1 exact-range review is rate-limited for 13 minutes and is explicitly not approval. A hosted exact-head full review and a different independent review are being requested. Synthetic fixtures only; no native Windows/live SCCM acceptance is claimed.

@adamgell

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

Please review the full exact-head range a8a367c661cd17bd1e73b87724c143ec8565ffc7..1e5a3174ceaaf2f7d045d4e36a4e1955a8b06f91. Focus on evidence binding, missing/partial coverage ceilings, profile/key validation, source-phase isolation, coherent chronology/recovery, privacy, deterministic output, pure Rust/wasm compatibility, and ensuring client-only output cannot claim an MP/server cause. Status-only, skipped, stale, or rate-limited responses will not be treated as approval.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

@adamgell I will perform a full review of the exact-head range. I will assess the requested safety boundaries and implementation constraints.

✅ Action performed

Full review finished.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 3 minutes.

@adamgell

Copy link
Copy Markdown
Owner Author

Independent exact-head review active in fresh detached worktree .worktrees/review-321-1e5a-independent at 1e5a3174ceaaf2f7d045d4e36a4e1955a8b06f91. This reviewer is read-only and will not edit or push. Scope: full PR range plus adversarial exact-key uniqueness, evidence-identity collision, marker delimiters, latest phase state, timestamp provenance, coverage non-outcomes, deterministic ordering, counterpart readiness, and workflow independence. A formal exact-head PASS/BLOCK with executable evidence follows; CodeRabbit status alone will not be treated as substantive approval.

@adamgell adamgell left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Independent exact-head review: BLOCK

Reviewed the full a8a367c..1e5a317 range in a fresh detached, read-only worktree. I made no edits or pushes.

Committed gates

  • Focused policy: 13/13 pass.
  • Shared spine: 136/136 pass.
  • Full cmtraceopen-parser suite: pass.
  • Strict parser Clippy: pass.
  • Rust 1.88 wasm32 check: pass.
  • TypeScript noEmit: pass.
  • Scoped Rustfmt and git diff --check: pass; worktree clean.
  • Workspace-wide cargo fmt remains red only on inherited files outside all eight PR-owned paths; every changed Rust file passes rustfmt.

Independent adversarial evidence

A disposable external harness imported all 13 committed policy tests, then ran six additional contract probes. Command: cargo test --test probe -- --nocapture. Result: 13 committed tests passed and all 6 adversarial probes failed against this exact SHA.

  1. Duplicate exact-key labels fail open: adding a second conflicting occurrence of each of AssignmentId, PolicyId, RequestId, ClientHandle, SiteCode, and SelectedManagementPointHostHandle still emits a high-confidence successful exact transaction.
  2. Compound markers fail open: not-Request succeeded-ish and an embedded NotRequestId label both emit the same high-confidence exact transaction.
  3. Conflicting facts with one evidence identity are input-order dependent: cloning the Report reference with the opposite terminal result yields high ConfirmedFailure in one order and high Success after reversing input, while the exported evidence references are identical after deduplication.
  4. Unusable chronology still proves a complete sequence: clearing utc_millis and marking every cross-artifact timestamp OffsetInvalid still emits high Succeeded at Report.
  5. Coverage provenance is collapsed: StateMessage AccessDenied plus captured CIAgent becomes a synthetic client-policy-state Partial gap, losing the explicit access-denied source state.
  6. Latest local state is not honored: a same-artifact Schedule Deferred followed by Schedule Succeeded resolves to Contradictory rather than the later recovered state.

Required before re-review

  • Route policy key admission through the frozen versioned extraction-profile contract, including full token boundaries and exactly one unambiguous occurrence per required label. A hard-coded profile string plus ad hoc first-match extraction is not an exact validated key.
  • Reject or conservatively quarantine conflicting facts that share one logical evidence identity before sorting/deduplication, and add a permanent reversal test.
  • Resolve coherent same-source latest state by source-local order, including Deferred to Succeeded recovery. Do not let missing or invalid cross-artifact time provenance prove a high-confidence request-to-report sequence.
  • Preserve each unavailable source state and citation; a captured sibling cannot erase an AccessDenied, Capped, Missing, or other explicit gap.
  • Move or freeze shared intake/model additions such as SccmNormalizedBundle and the shared Partial coverage variant in the upstream #318/#319 contract before merging #321. This workflow PR must consume stable shared interfaces rather than become the dependency base for unrelated reducers.
  • Promote all six probes into repository tests and show focused red-to-green evidence.

This is a substantive merge block, not a compile failure. The reducer remains pure Rust/wasm-compatible and makes no explicit MP causal claim, but its exact-key, evidence-identity, chronology, latest-state, and coverage guarantees are not yet conservative enough for counterpart-ready facts or downstream #333 correlation.

CodeRabbit note: the hosted exact-head attempt currently contains only a fair-usage/rate-limit response and a green status context, not substantive review findings. It must remain in the loop, but that status is not approval.

fn extract_label_token<'a>(message: &'a str, label: &str) -> Option<&'a str> {
let normalized = message.to_ascii_lowercase();
let marker = format!("{}=", label.to_ascii_lowercase());
let start = normalized.find(&marker)? + marker.len();

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

[P1] Exact-key extraction must fail closed on ambiguous or embedded labels. This first-match search accepts both a conflicting second RequestId and NotRequestId=... as the required RequestId, and equivalent duplicate mutations pass for every required key label while retaining high confidence. Please use the frozen versioned key-profile extractor or equivalent exact-boundary, exactly-once validation, with permanent duplicate/embedded-label tests.

if same_artifact {
return facts
.iter()
.max_by(|left, right| compare_evidence_refs(&left.reference, &right.reference))

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

[P1] Validate logical evidence identity before choosing a latest outcome. Two opposite terminal facts with the same artifactId, entryId, and line range compare equal here; max_by then selects according to input order. Reversing that input flips the public result between high ConfirmedFailure and high Success even though deduplicated evidence is identical. Reject/quarantine identity collisions before reduction and add the reversal regression. The same latest-state path should also allow later same-artifact Deferred to Succeeded recovery.

for later in facts.iter().filter(|fact| fact.phase <= current_phase) {
if earlier.phase >= later.phase
|| earlier.reference.artifact_id == later.reference.artifact_id
|| !earlier.time_comparable

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

[P1] Skipping non-comparable cross-artifact phases removes the only chronology guard, after which a complete set of markers is still emitted as high Succeeded at Report. A probe with utcMillis absent and OffsetInvalid on every phase reproduces this. Missing or invalid provenance must cap the sequence or make it explicitly incomplete; it cannot prove an ordered request-to-report path.

.filter(|artifact| policy_group(&artifact.display_name) == Some(logical_id))
.map(|artifact| artifact.coverage.clone())
.collect::<Vec<_>>();
if states.is_empty() || states.contains(&SccmCoverageState::Captured) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

[P1] A captured sibling must not erase an explicit coverage gap. With CIAgent captured and StateMessage AccessDenied, this early return discards the access-denied state and the caller synthesizes client-policy-state Partial. Preserve source-specific coverage/citations so AccessDenied, Capped, Skipped, Unsupported, malformed, and missing remain observable non-outcomes.

/// The bundle owns no raw file handles or collection behavior. Its evidence has
/// already passed through the shared CCM logical-record scanner.
#[derive(Debug, Clone, PartialEq)]
pub struct SccmNormalizedBundle {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

[P1] This is an intake/shared interface, not policy workflow behavior. The execution contract makes #321 a consumer of frozen #318/#319 contracts; introducing SccmNormalizedBundle here forces unrelated reducers to stack on the policy branch. Land and review this bundle contract upstream, together with any shared coverage-model addition, then rebase this issue-scoped reducer onto that public API.

@adamgell

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

Please perform a substantive exact-head review of a8a367c..1e5a317 after the prior fair-usage response. An independent review has now posted six reproducible blocker classes and five inline anchors. Please evaluate the exact-key uniqueness/boundaries, logical evidence-identity collisions, input-order determinism, latest same-source recovery, invalid cross-artifact time provenance, source-specific coverage preservation, and the #318/#319 shared-interface dependency. A green status context without findings is not approval.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

@adamgell I will perform a substantive full review of the exact-head range.

I will assess exact-key boundaries, evidence identity, deterministic ordering, recovery state, timestamp provenance, source-specific coverage, the #318/#319 interface contract, wasm compatibility, privacy, and the client-only causality boundary.

✅ Action performed

Full review finished.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 31 minutes.

@adamgell adamgell added feature New feature sccm SCCM/ConfigMgr related parser Log parser related enhancement New feature or request labels Jul 31, 2026
@adamgell

adamgell commented Aug 1, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 38 minutes.

@adamgell adamgell left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

BLOCK at exact head 1fb7a86.

Battery at 1fb7a86: sccm_client_policy 22 passed; sccm_spine_contract 136
passed; cargo test --locked -p cmtraceopen-parser 751 passed, 0 failed;
cargo clippy --locked --workspace --all-targets -- -D warnings exit 0;
cargo check --target wasm32-unknown-unknown exit 0; rustfmt --check clean on
all nine changed .rs files. cargo test --locked --workspace is green apart
from src-tauri/tests/cmtlog_parser.rs, a pre-existing shared-temp-dir race
that passes with --test-threads=1 and on three reruns; this PR touches no
file under src-tauri.

RED discipline verified for all six pairs by replaying each RED at its own
commit: 3e24f79 3/16, 246bef2 2/18, c8ca95b 1/20, d0d5153 1/21,
f2c9e70 1/22, 7e4ef4d 1/22. Every failure is an assertion whose message
matches the reason its test name claims.

Per-closure adjudication:

  1. Exact-key admission: PARTIAL. See gaps 3 and 6.
  2. Identity collisions: CLOSED. Equal claims are not quarantined, a
    three-way collision is, and both are order independent.
  3. Chronology: CLOSED. All-OffsetInvalid no longer yields high Succeeded,
    partial comparability caps in every single-source variant with the right
    group and reason, and the anti-over-reach case holds.
  4. Coverage gaps: CLOSED for the scenario the thread named. See gap 5.
  5. Scope split: NOT CLOSED as stated. Detail below.
  6. Cross-role: PARTIAL. See gaps 1 and 2.
  7. Recapture reason: PARTIAL. See gap 4.

Gaps, each reproduced:

  1. [P1] group_has_no_captured_source (policy.rs:1185-1194) is not filtered
    to SccmRole::Client. Adding a ManagementPoint artifact named
    LocationServices.log with Captured coverage to request-auth-failure
    erases the client-location coverage gap and its artifact request and
    raises the transaction from confirmedFailure/medium to
    confirmedFailure/high. The inverse leaks too: an MP artifact at
    AccessDenied creates a client-location gap and a client artifact request
    that the client-only bundle does not have. coverage_gaps_for_group
    (line 1142) does filter on role, so this is inconsistent within the
    same feature.

  2. [P1] The last-wins artifact_by_id collect (policy.rs:205-210) is still
    decided by artifact vector order when two Client artifacts share one
    artifact_id. With a second client artifact reusing
    policy-complete-agent-current as CIDownloader.log/AccessDenied, the
    forward bundle yields zero transactions plus a malformed symptom and the
    reversed bundle yields high-confidence succeeded. f2c9e70 passes only
    because it shadows with a different role.

  3. [P1] is_label_boundary_start (line 927) uses is_label_token_boundary
    (line 944), the value terminator set, as its left word-boundary test. A
    conflicting occurrence preceded by [ ( = / # | { or * is neither
    admitted nor counted as ambiguity. Appending
    context=[RequestId={99999999-9999-9999-9999-999999999999} retry=1] to
    the complete-scenario request record still produces one transaction,
    state succeeded, confidence high, keyed on the first RequestId.
    Terminator-prefixed duplicates all fail closed, which shows the
    asymmetry is unintended.

  4. [P2] request_for_group (line 1096) keys the reason on the phase alone,
    so a MISSING Schedule phase is requested with "Recapture bounded
    scheduler evidence for the deferred policy retry." Removing the Schedule
    record from the complete scenario reproduces it. Same class as closure 7.

  5. [P2] A non-captured sibling is invisible once the chain succeeds. With
    StateMessage.log captured and a StatusAgent.log sibling at AccessDenied,
    Capped, Skipped, Unsupported, or ParseFailed, coverageGaps is empty in
    all five cases. Closure 4 works only because losing the source also
    loses the phase.

  6. [P3] is_marker_boundary (line 758) tests raw bytes, so a UTF-8
    continuation byte reads as a separator and "cafeRequest succeeded" (with
    an e-acute) is admitted as the exact "Request succeeded" marker. Same
    class as the not-Request case.

On closure 5: sccm/ingest.rs is genuinely pre-existing in a8a367c,
0df7f42 is the only commit in this PR that touches it, the public path
sccm::SccmNormalizedBundle is unchanged, and nothing outside the PR
referenced the type. The objection is still open on its own terms.
0df7f42 also deletes from client/mod.rs and edits policy.rs, both files
this PR creates, so it cannot be applied to the base and no other reducer
can consume the contract without stacking on this branch, which is the harm
the thread named. The thread also asked for any shared coverage-model
addition to land upstream, and SccmCoverageState::Partial in models.rs plus
the coverage_state_order renumbering in findings.rs remain in this PR.
Separately, health.rs is not present in this tree, so the pattern cited by
closures 4 and 6 could not be cross-checked here.

Privacy: clean. A canary sweep injecting a hostname, a C:\Users path, and a
message payload across six scenarios leaked nothing into the serialized
analysis, and the diff sweep over 1e5a317..HEAD shows no PII, host, path,
or credential material.

Non-blocking note: opposite claims at the same artifact and line range but
different entry ids are resolved by a lexicographic entry-id tiebreak. It is
deterministic and lands conservative in the case probed, but the winner is
chosen by string sort rather than by evidence.

Recommend gaps 1, 2, and 3 be closed RED-first before merge, since PR #392
and the #333 correlation work depend on these facts being stable. Gaps 1 and
2 in particular are the same defect classes closures 6 and 2 were opened
for, at sites the frozen tests do not reach.

@adamgell
adamgell force-pushed the codex/sccm-321-policy-analysis branch from 1fb7a86 to d6c2bc3 Compare August 1, 2026 13:17
@adamgell
adamgell requested a review from Copilot August 1, 2026 13:27

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 an SCCM client-side policy workflow analyzer to cmtraceopen-parser, along with a new synthetic-corpus contract test suite and supporting plumbing (normalized bundle type, coverage state expansion, and catalog entries). This extends the SCCM diagnostics layer to model policy request→download→persist→schedule→evaluate→report phases and emit transactions, observations, findings, coverage gaps, and artifact requests.

Changes:

  • Introduce sccm::client::policy reducer (analyze_client_policy) and its public analysis model types.
  • Add a comprehensive sccm_client_policy integration test using the 14-scenario synthetic policy fixture corpus.
  • Expand SCCM coverage/state/catalog support for the policy workflow (new Partial coverage state + new policy-related catalog tuples).

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
crates/cmtraceopen-parser/tests/sccm_spine_contract.rs Extends spine contract expectations for the new Partial coverage state and additional policy-related catalog tuples.
crates/cmtraceopen-parser/tests/sccm_client_policy.rs New integration test suite defining the frozen public behavior contract for client policy analysis across 14 scenarios.
crates/cmtraceopen-parser/src/sccm/models.rs Adds SccmCoverageState::Partial to the shared SCCM model.
crates/cmtraceopen-parser/src/sccm/mod.rs Exposes the new sccm::client module and re-exports it from sccm.
crates/cmtraceopen-parser/src/sccm/ingest.rs Introduces SccmNormalizedBundle as the normalized SCCM analyzer input contract.
crates/cmtraceopen-parser/src/sccm/findings.rs Updates coverage-gap ordering to account for the new Partial state.
crates/cmtraceopen-parser/src/sccm/client/policy.rs New client policy reducer implementation (transaction reduction, evidence/key extraction, findings, coverage gaps, and artifact requests).
crates/cmtraceopen-parser/src/sccm/client/mod.rs New client module wrapper re-exporting the policy analyzer.
crates/cmtraceopen-parser/src/sccm/catalog.rs Adds CIAgent/CIDownloader/StateMessage/StatusAgent to the declared SCCM source catalog under ClientPolicy.

Comment on lines +909 to +912
for earlier in facts.iter().filter(|fact| fact.phase <= current_phase) {
for later in facts.iter().filter(|fact| fact.phase <= current_phase) {
if earlier.phase >= later.phase
|| earlier.reference.artifact_id == later.reference.artifact_id
Comment on lines 1 to 3
pub mod catalog;
pub mod client;
mod evidence;
@adamgell

adamgell commented Aug 1, 2026

Copy link
Copy Markdown
Owner Author

Code review

Five independent reviewers examined this change from different angles (CLAUDE.md compliance, focused bug scan, git-blame history, prior-PR feedback, and code-comment guidance). Each candidate finding was then independently verified and confidence-scored; anything below 80 is reported separately as unconfirmed.
13 confirmed finding(s) (score >= 80), 4 unconfirmed, 1 rejected as false positives.

1. Cross-phase time inversion emits one finding spanning two unrelated logical groups, violating the suite's own frozen invariant

cmtraceopen-parser/src/sccm/client/policy.rs lines 450-459 at head d6c2bc3 (helper 904-929); candidate cited 433-442/821-846 which is the stale 1fb7a86 tree (confidence 85)
PRIMARY CLAIM CONFIRMED at runtime. I replayed the exact mutation of policy_cross_artifact_phase_time_inversion_is_contradictory (complete fixture, Report utc_millis = request_utc - 1) and got: transaction nextArtifacts = [client-policy-agent, client-policy-state]; finding finding:policy:report:1111... nextArtifacts = [ciAgent, policyAgent, stateMessage]; distinct groups in ONE finding = 2. That is exactly the shape actual_finding_signatures asserts against at tests/sccm_client_policy.rs:464 (assert!(groups.len() <= 1)) and again at :1103. The full 34-test suite is green, so the invariant is only unenforced because no fixture or mutation test runs the group check on an inversion. Deducted 15: the stated SECONDARY defect is stale/false at head. d6c2bc3 replaced request_for_group with request_for_cause, so the emitted reason is 'Recapture bounded policy-agent evidence with a valid timestamp offset...', not the missing-phase wording the candidate quotes, and chronology_request_for_group no longer exists.

Verified by probe against head. cross_phase_time_inversion_groups inserts required_group_for_phase for both sides of every inverted pair; the caller loops them all into next_artifacts; specific_finding_requests (1428-1465) expands POLICY_STATE_GROUP into two requests, so one finding carries three requests across two groups. next_artifacts.dedup() at 483 cannot help since the entries differ.

2. An entirely uncollected client-location group yields HIGHER confidence than one recorded as absent

cmtraceopen-parser/src/sccm/client/policy.rs lines 1257-1261 (guard), 418-427 (call site) (confidence 90)
CONFIRMED at runtime, output matches the candidate's report exactly. request-auth-failure as shipped: confidence=medium, coverageGapArtifactIds=["client-location"], finding confidence=moderate with a clientLocation request. Same bundle with the single policy-auth-location-absent artifact removed: confidence=HIGH, coverageGapArtifactIds=[], finding confidence=high, nextArtifacts=[]. Deleting information from the bundle strengthens the claim and silently deletes the only actionable repair. This is a conservatism inversion in a reducer whose entire stated design is fail-closed, it lands on confidence and nextArtifacts (the facts #333 correlation consumes), and the empty-group shape is the DEFAULT: the shipped complete fixture carries no client-location artifact at all. Only reason it is not 100 is that reaching it needs a Request-phase confirmed failure.

client_group_artifacts(...).peekable() short-circuits on peek().is_some(), so zero artifacts returns false ('a captured source exists'). Its documented citation twin coverage_gaps_for_group (1219-1225) maps the same empty group to SccmCoverageState::Absent — the two disagree about total absence.

3. Ambiguous-artifact-id quarantine runs before the policy-scope check, so non-policy evidence becomes policy findings

cmtraceopen-parser/src/sccm/client/policy.rs lines 223-238 (ambiguity branch 227-232, scope gate 236) (confidence 80)
CONFIRMED at runtime, output matches the candidate's JSON. Bundle = request-auth-failure plus two client AppEnforce.log artifacts sharing id shared-app-id (differing coverage) plus one evidence record on that id. Result: sourceLocalObservations gains policy:source-local:malformed citing shared-app-id:1-1 and requesting client-policy-agent, and findings gains finding:policy-malformed ('Policy evidence did not match a validated profile') whose only repair is 'Collect the complete PolicyAgent file.' A non-duplicated AppEnforce record is correctly dropped by is_admitted_policy_artifact, so the two paths genuinely disagree about scope. admissible_client_artifacts (567-594) never consults policy_group. Held to 80 rather than higher because the intake contract asserts distinct artifact ids, so this is a defensive path — but it is a path this PR deliberately added, wrote a test for, and the reducer's output is a fabricated policy symptom plus a wrong repair.

policy_duplicate_client_artifact_id_is_never_resolved_by_vector_order (tests:1246) renames its clash to CIDownloader.log, still in a policy group, so it does not cover this.

4. Repair requests are derived from the phase, not from the source that holds it: Scheduler.log and PolicyEvaluator.log can never be requested

cmtraceopen-parser/src/sccm/client/policy.rs lines 1154-1162, 1428-1465; call sites 392-394, 450-473 (confidence 85)
CONFIRMED at runtime on all three sub-cases. (1) Schedule missing (PolicyAgent-only bundle): phase=persist, gaps=[client-policy-agent], finding request = 'Collect the complete PolicyAgent file.' — PolicyAgent.log is fully captured and Scheduler.log, the file that would supply the phase, is never named, so acting on the request cannot close the gap. (2) scheduler-deferred fixture as shipped: workflow request correctly says 'Recapture bounded scheduler evidence for the deferred policy retry.' while the finding-level request contradicts it with 'Collect the complete PolicyAgent file.' (3) An Evaluate fact carried by PolicyEvaluator.log with an unusable offset produces gaps=[client-policy-state] and requests CIAgent + StateMessage. I confirmed specific_finding_requests has only three arms and that catalog.rs:161-203 already declares scheduler/policyEvaluator/ciDownloader/statusAgent/policyAgentProvider, so correct requests are expressible today. A missing Scheduler.log is the single most ordinary real-world shape here, and the request is the reducer's only actionable output.

The frozen tests project requests back to their group in actual_finding_signatures, which is exactly why the file-level mismatch is invisible.

5. "Name the source that broke chronology" names it by phase, so a CIDownloader or PolicyEvaluator break points at the wrong log family

cmtraceopen-parser/src/sccm/client/policy.rs lines 904-965, 1039-1055, 1071-1080, 1154-1162 (confidence 80)
CONFIRMED at runtime, both divergent cases. Bundle with PolicyAgent (Request/Persist good), CIDownloader carrying Download with ordering_state != NormalizedUtc, CIAgent, StateMessage: output is coverageGapArtifactIds=["client-policy-agent"], request 'Recapture bounded policy-agent evidence with a valid timestamp offset', finding gap client-policy-agent: partial, finding request 'Collect the complete PolicyAgent file.' CIDownloader.log — the file whose timestamps are unusable — is neither named nor cited. Symmetric case with a bad-offset PolicyEvaluator.log Evaluate fact blames client-policy-state and asks for CIAgent + StateMessage. The three-mapping analysis (source_allows_phase vs policy_group vs required_group_for_phase) is accurate as written. Scored just under #6 because it shares that root cause and the triggering shape (a Download record in CIDownloader.log) is less common than a missing Scheduler.log.

PolicyFact carries no artifact group or display name, so the doc claim at 931 ('the group holding the earliest source whose time provenance breaks the chain') is not something the data can support.

6. Ambiguous-artifact-id rejection runs before policy scoping, so unrelated client logs become policy findings

cmtraceopen-parser/src/sccm/client/policy.rs lines 223-250 (confidence 80)
CONFIRMED — same defect as the other two ambiguity candidates, reproduced at runtime (duplicated AppEnforce id yields policy:source-local:malformed + finding:policy-malformed + a PolicyAgent recapture request). This write-up adds two accurate details the others do not: the ordering guarantee genuinely regressed in bcff5b9 (before it, the scope check was the first gate and out-of-scope evidence could never reach rejected_policy_evidence), and SccmFinding.evidence has no cardinality bound (I confirmed validate_artifact_requests caps next_artifacts via MAX_SCCM_NEXT_ARTIFACT_REQUESTS while validate_coverage_gaps/evidence have no count bound), so a large duplicated log dumps every record into one finding. Its claim about the existing test using CIDownloader.log is correct. Same 80 as the sibling reports since it is the same underlying bug on a defensive path.

admissible_client_artifacts computes ambiguity across all client-role artifacts with no policy_group consultation.

7. Success markers are never checked against an explicit terminal result code (one-sided check; the exact class fixed in PR #393)

cmtraceopen-parser/src/sccm/client/policy.rs lines 689-691 (confidence 90)
CONFIRMED at runtime, and the sibling-precedent claim checks out. Appending Result=0x80070005 to the Report succeeded line of the complete fixture yields state=succeeded, classification=success, confidence=high, findings=[] — a High-confidence proven policy chain whose terminal record carries an explicit failure HRESULT, with build_transaction_finding returning None for Success so nothing surfaces at all. I verified the merged sibling on the base branch (origin/codex/parser-family-skeleton, management_point.rs:828-833) implements the two-sided validated_result_value check, so the class was genuinely closed there and is one-sided here. has_terminal_failure_signal (882-902) is only ever called under outcome == FactOutcome::Failed. Realistic: CCM records routinely carry a Result/hr/gle token on the same line, and marker/result disagreement is exactly what a mis-instrumented component produces. Highest-impact defect in the set alongside #4.

policy_failure_requires_a_nonzero_terminal_result (tests:594) covers only the failure direction; the inverse regression is absent.

8. Evidence quarantine only catches identical line ranges, not overlapping physical intervals (accepted P1 class from PR #393)

cmtraceopen-parser/src/sccm/client/policy.rs lines 596-642 (confidence 80)
CONFIRMED at runtime. I cloned the complete fixture's Report succeeded record (lines 1-1) into a Report failed terminal ... Result=0x80070005 record with a different entry_id and lines 1-2. is_safe_evidence_reference passes, the identity tuples differ so quarantine_identity_collisions does nothing, both facts reach the Report phase, and latest_comparable_outcome's same_artifact branch picks the 1-2 record purely because compare_evidence_refs ranks the wider range higher. Output: state=failed, classification=confirmedFailure, confidence=HIGH, with terminal evidence physically overlapping the success record it contradicts. I verified the merged MP reducer on the base branch implements evidence_references_overlap at management_point.rs:1130-1144 and rejects any record participating in an overlap, so the class was closed there. Held at 80 because it requires malformed intake (overlapping logical records from one artifact) rather than ordinary logs.

The same hole lets one physical line prove two different phases, since nothing prevents e.g. PolicyAgent 1-2 Request and 2-3 Persist from both being admitted.

9. Cross-artifact time inversion still fans out to every inverted group, so one finding requests two unrelated log families

cmtraceopen-parser/src/sccm/client/policy.rs lines 450-474 (confidence 85)
CONFIRMED at runtime, and this write-up is the most accurate of the three inversion reports: it targets head line numbers, correctly identifies that the narrowing was applied only to the else if arm while the if arm three lines above still loops, correctly notes next_artifacts.dedup() (483) only drops consecutive equals so both survive, and correctly traces specific_finding_requests expanding two groups into policyAgent + ciAgent + stateMessage. My replay of policy_cross_artifact_phase_time_inversion_is_contradictory's own mutation produced exactly that: one finding, three requests, two distinct groups — the state the suite asserts against twice (assert!(groups.len() <= 1) at tests:464 and :1103) and which the mutation test never checks because it only asserts state/classification/confidence. The 'clock skew between CCM components is common in real captures' framing is fair; this is not a synthetic-only path.

Verified full suite (34 tests) green at head, so the invariant is genuinely unenforced on this branch.

10. Ambiguous client artifact ids are rejected before the policy-scope gate, so duplicated non-policy client logs become finding:policy-malformed with a PolicyAgent recapture request

cmtraceopen-parser/src/sccm/client/policy.rs lines 223-250, helper 567-594, scope gate 1082-1089 (confidence 80)
CONFIRMED — third report of the same real defect, reproduced at runtime as described. Its head line numbers are right, its reading of admissible_client_artifacts is right, and it adds one aggravator the others miss that I verified independently: the ambiguity branch also bypasses the coverage == Captured half of is_admitted_policy_artifact, so evidence attached to a non-captured artifact is escalated too. Its severity self-assessment is honest (bounded by the intake distinctArtifactIds contract, so defensive). Same 80 as its duplicates.

Reproduced output: policy:source-local:malformed citing AppEnforce evidence and finding:policy-malformed requesting policyAgent.

11. Terminal failure evidence past the first missing phase disappears from the analysis with no trace

cmtraceopen-parser/src/sccm/client/policy.rs lines 390-396, 485-490 (confidence 80)
CONFIRMED at runtime with the candidate's exact scenario. Bundle = PolicyAgent Request succeeded + CIAgent Evaluate failed terminal ... Result=0x80070005, no Download source. Full emitted JSON: one transaction at phase=request, state=incomplete, classification=insufficientEvidence, confidence=medium, evidence=[a-agent:1-1] only; sourceLocalObservations=[]; one Warning-severity finding with terminalEvidence=[]; coverageGaps=[client-policy-agent/partial]. The confirmed terminal Evaluate failure appears in NO output field. I walked the four escape hatches the candidate names (rejected_policy_evidence only fills when reduce returns None, partial_policy_artifacts excludes ids in normalized_evidence_ids, unavailable_client_policy_sources excludes Captured artifacts) and all four genuinely miss it. The scenario — one uncollected log mid-chain plus a downstream failure — is ordinary, and the output actively understates an Error-severity confirmed failure as a moderate evidence gap.

The candidate's framing is right that not citing the record inside the transaction is defensible while discarding it entirely is not; the rejected/rotation-split observation mechanism already exists for this.

12. group_has_no_captured_source answers "a captured source exists" for a group with zero artifacts, so a Request-phase failure keeps High confidence when client-location was never collected at all

cmtraceopen-parser/src/sccm/client/policy.rs lines 1252-1261 (helper), 418-427 (call site) (confidence 90)
CONFIRMED, same defect as the other empty-group report, and this is the better-argued version of it. Its reproduction is exactly what I measured (drop policy-auth-location-absent and confidence goes medium -> high, coverageGapArtifactIds empties, the client-location request disappears). Two supporting claims I verified independently and both hold: coverage_gaps_for_group (1219-1225) really does map the empty group to Absent while its documented twin maps it to 'has a captured source', so the two functions the doc comment ties together disagree about total absence; and SccmArtifact::missing — the only constructor that synthesizes Absent placeholders — is referenced solely from tests/sccm_spine_contract.rs:3543 and never from production intake, so nothing guarantees the placeholder the confidence cap depends on. The shipped complete fixture carrying no client-location artifact confirms the empty-group shape is the norm. Its proposed fix (drop the peek guard, or split the cap from the citation) is correct.

Reproduced both directions; the fixture's confidenceCeiling: medium contract is defeated by removing information rather than adding it.

13. Cross-phase time-inversion requests ask for "a valid timestamp offset" even though both records are NormalizedUtc, asserting a state the evidence does not have

cmtraceopen-parser/src/sccm/client/policy.rs lines 450-459, 904-929, reasons 1175-1204, RequestCause doc 190-203 (confidence 85)
CONFIRMED at runtime, and it lands squarely on what the head commit claimed to close. My inversion replay emitted artifactRequests = ['Recapture bounded policy-agent evidence with a valid timestamp offset; do not order by display time.', 'Recapture bounded policy-state evidence with a valid timestamp offset...'] on the complete fixture, where every record is +000 NormalizedUtc. cross_phase_time_inversion_groups skips any pair where !time_comparable, and time_comparable is set only for NormalizedUtc with utc_millis.is_some(), so by construction both offsets are valid — the reducer asserts they are not at the one moment its own state proves they are. That directly contradicts d6c2bc3's own RequestCause doc ('a reason can never assert a state the evidence does not have'), which is the commit that introduced the cause enum. The contrast with contradictory-offset (OffsetInvalid, routed through unprovable_chronology_group) is accurate, and the observation that the remedy is not actionable — recapture returns the same valid offsets and the same inversion — is the substantive point. Deducted 15 only because it overlaps #1/#13 on the group fan-out half.

policy_cross_artifact_phase_time_inversion_is_contradictory (tests:619-639) asserts only state/classification/confidence, never artifactRequests, which is why the cause-derivation pass missed it.


Unconfirmed findings (scored 50-79, verify before acting)
  • [55] Generic SccmWorkflow* contract is hardwired to policy shapes yet exported flat and frozen at schemaVersion 1
  • [70] unavailable_client_policy_sources uses policy_group(..).is_some(), widening "policy source" past the agent/state definition and past the ambiguity guard
  • [75] counterpartReadyFact can hand SCCM: correlate client and server evidence into conservative causal findings #333 a Request record whose outcome contradicts the transaction verdict
  • [60] Rotation-split finding is inferred from a four-basename logical group, so it can claim a split between unrelated files

@adamgell
adamgell requested a review from Copilot August 1, 2026 14:34
adamgell added 19 commits August 2, 2026 16:14
Freeze that any non-word character starts a countable label
occurrence, that a word character never does, and that phase
markers judge neighbours as characters rather than raw bytes.

Refs #321
Define a boundary as the absence of a word character instead of
an enumerated separator list, so punctuation nobody listed can
no longer hide a duplicate label. Compare characters rather than
raw bytes so a multibyte letter cannot pose as a separator, and
stop reusing the value-terminator set as a left boundary.

Refs #321
Freeze that an out-of-scope artifact sharing a policy basename
neither answers a client coverage question nor invents a client
gap, in both the captured and unavailable directions.

Refs #321
Route every logical-group coverage query through one client-role
selector so an out-of-scope artifact sharing a policy basename
can neither satisfy a client coverage question nor invent a
client gap.

Refs #321
Freeze that two client artifacts sharing one id are ambiguous
rather than order dependent, stay observable, and cannot carry
a high-confidence verdict, while an exact repeat is harmless.

Refs #321
Detect duplicate ids within the client role and withhold every
id no artifact can speak for, so admission is never decided by
artifact vector order. Ambiguous records stay observable as a
local symptom. An exact repeat is not a conflict.

Refs #321
Freeze that an unavailable client policy source stays a cited
non-outcome even when the transaction proves a complete chain,
without vetoing it, and that a fully captured bundle stays gap
free.

Refs #321
Report every client policy source that is not fully captured as
a coverage gap regardless of the transaction verdict, so a
sibling that happened to carry the chain can no longer hide an
explicit non-outcome.

Refs #321
Freeze that a missing phase is not asked for as a deferred
retry and that a contradiction caused by unusable time asks for
usable time, while a genuine deferral keeps its own wording.

Refs #321
Key every artifact request on the state that caused it instead
of on the phase, so a missing phase is no longer asked for as a
deferred retry. Carry the contradiction cause out of phase
resolution so an unorderable phase asks for usable time.

Refs #321
Two probes the reducer currently answers the wrong way round. Removing
the only client-location record raises confidence from medium to high
and deletes the repair request, and a confirmed terminal failure past
the first missing phase leaves no trace in any output field.

Refs #321
An empty client-location group answered "a captured source exists", so
deleting the only record of an absence raised confidence from medium to
high and dropped the repair request. The guard now agrees with its
citation twin, which already maps the empty group to absent.

A confirmed terminal failure recorded past the first missing phase is no
longer discarded. It leaves the reduction as a source-local observation
and an error-severity finding instead of vanishing behind a moderate
evidence gap.

Refs #321
The success side of the terminal result check and the overlapping half
of evidence quarantine are both open here. A success or deferral marker
carrying an explicit failure code still proves its phase, and a wider
overlapping line range still outranks the record it overlaps.

Refs #321
A marker and an explicit terminal result that disagree can no longer be
read either way, so a success or deferral carrying a failure code stops
proving its phase, mirroring the existing failure-side check.

Evidence quarantine now compares physical line extent instead of an
identity tuple, so a wider overlapping range no longer outranks the
record it overlaps and one physical line can no longer prove two phases.

Refs #321
Six probes for requests derived from the phase rather than the artifact
holding the evidence. Scheduler.log and CIDownloader.log can never be
named, a deferral asks for PolicyAgent, an inversion between two valid
offsets claims the offset is invalid and fans out across two families,
and an ambiguous non-policy client id becomes a policy finding.

Refs #321
Every repair now carries the declared sources that would supply it, read
off the artifact rather than guessed from the phase. Scheduler.log and
CIDownloader.log become requestable, a deferral asks for the file that
deferred, and a chronology break names the family that broke it.

A cross-phase inversion names one broken link with its own cause, so it
no longer claims a valid normalized offset is unusable and no longer
spans two log families in one finding. A break also replaces the missing
phase request instead of joining it, since a phase can only be called
missing once the surrounding phases can be sequenced.

Ambiguous client artifact ids are only escalated when the clash costs
this reducer an admissible policy source, so a duplicated AppEnforce id
no longer becomes a policy finding.

Refs #321
Scheduler.log and PolicyEvaluator.lo_ are not fragments of one another,
yet the reducer reports them as a split PolicyAgent record because the
candidate set is the whole logical group.

Refs #321
Split candidates are grouped by their declared basename, so only files
that could actually be rotations of the same record are compared. An
unrelated agent-group sibling no longer turns into half a torn
PolicyAgent record.

Refs #321
The rebase onto codex/parser-family-skeleton applied cleanly and did not
build. Three independent collisions, each from both sides adding the same
contract separately:

SccmCoverageState::Partial is introduced by this lane, and the skeleton
independently added seven exhaustive matches over that enum. Place Partial
semantically in each rather than widening any match to a wildcard: it
carries real evidence, so it outranks every noncaptured state and loses
only to a complete capture, matching findings::coverage_state_order, which
is the canonical ordering. Rank maps, sort keys, rejection reasons, and
operator wording each get the placement their own function means.

SOURCE_CATALOG and its frozen expected-tuple list each carried a
duplicated run of CIAgent, CIDownloader, StateMessage, and StatusAgent:
the skeleton already declares every client-policy entry this lane wanted
to add, in a different order, so git appended rather than merged them. Drop
the duplicate run from both sides of the contract.

SccmClientWorkflow was defined in both client/intake.rs and client/policy.rs,
so the two glob re-exports in client/mod.rs were ambiguous. The intake
definition is the shared four-variant client contract and the policy one was
a single-variant stub predating it; policy now imports the shared type.
Every client reducer must agree on one set of workflow names or identical
evidence sorts differently per lane.

Refs #321
@adamgell
adamgell force-pushed the codex/sccm-321-policy-analysis branch from 1bdcd9c to 486e982 Compare August 2, 2026 20:35
@adamgell
adamgell marked this pull request as ready for review August 3, 2026 05:47
@adamgell
adamgell requested a review from Copilot August 3, 2026 05:47

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 11 out of 11 changed files in this pull request and generated no new comments.

Suppressed comments (3)

crates/cmtraceopen-parser/src/sccm/client/policy.rs:443

  • policy:source-local:malformed always requests client-policy-agent, but rejected_policy_evidence can include policy-state artifacts too (e.g., invalid profile/version or ambiguous parsing from CIAgent/StateMessage). This can produce a remediation hint that names the wrong group and even says “policy-agent evidence” when the rejected evidence is policy-state-only.
        let request = workflow_request(
            POLICY_AGENT_GROUP,
            "Capture bounded policy-agent evidence under a validated ConfigMgr version profile with a complete exact key.",
        );

crates/cmtraceopen-parser/src/sccm/client/policy.rs:321

  • The PR description says this commit adds only the RED test contract and that analyze_client_policy / SccmNormalizedBundle “do not exist yet”, but this PR adds both the production reducer (analyze_client_policy) and the normalized bundle type. Please update the PR description (and/or remove the outdated CodeRabbit summary) so reviewers understand the true scope of the changes.
pub fn analyze_client_policy(bundle: &SccmNormalizedBundle) -> SccmWorkflowAnalysis {

crates/cmtraceopen-parser/src/sccm/client/intake.rs:1520

  • The comment above SccmCoverageState::Partial is misleading here: group_coverage() uses max_by_key(coverage_rank), so higher rank values represent worse coverage. Also, this ranking does not fully match findings::coverage_state_order (e.g., Unsupported/Skipped are ordered differently), so calling that “canonical” is inaccurate.
        // Partial carries real evidence, so it outranks every noncaptured
        // state while still losing to a complete capture. This mirrors
        // `findings::coverage_state_order`, which is the canonical ordering.

@adamgell

adamgell commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

Execution gate — rebuild required

This PR is now draft and must not merge from its current head 486e982f.

Compared with the current SCCM integration head 83295227, this stack spans 37 files and would delete the sealed client-admission authority/tests, Distribution Point reducer/tests, private capture destination, and newer server-intake contracts. The policy reducer still accepts caller-constructible SccmNormalizedBundle rather than canonical admitted evidence, and no reviewed exact/strong ClientPolicy extraction profile exists.

Two additional reducer/test gaps remain reproducible: unrelated URL status= text can override the terminal policy result, and group-level artifact requests can disappear from the test oracle. Green stale-head CI does not close these architecture blockers.

Resume gate: create a fresh #321 branch from the then-current codex/parser-family-skeleton after a reviewed exact policy extraction profile exists. Port only the applicable fixtures/reducer behavior, consume sealed admitted evidence, preserve explicit coverage states, add bounded/adversarial regressions, and run exact-range review. Do not rewrite or force-push this evidence branch.

@adamgell

adamgell commented Aug 5, 2026

Copy link
Copy Markdown
Owner Author

Superseded on main. Current policy analysis uses sealed intake plus payload authority, has a 14-scenario exact production oracle and chronology/authority regressions; latest lane correction dcc8e56. The retired integration head was promoted and hardened through PR #490. Closing unmerged.

@adamgell adamgell closed this Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request feature New feature parser Log parser related sccm SCCM/ConfigMgr related test Testing related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants