Skip to content

fix(intune): Store reducer semantic hardening (Framework v1 Phase 3) - #532

Merged
adamgell merged 8 commits into
mainfrom
store/reducer-phase3-fixes
Aug 8, 2026
Merged

fix(intune): Store reducer semantic hardening (Framework v1 Phase 3)#532
adamgell merged 8 commits into
mainfrom
store/reducer-phase3-fixes

Conversation

@adamgell

@adamgell adamgell commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Reducer Framework v1 Store pilot, Phase 3 ("PR 3" in docs/superpowers/plans/2026-08-07-reducer-framework-v1.md): the concrete fixes for the three Phase 2 RED defects, plus the adversarial pilot over the remaining inventory clusters. Stacks on the Phase 2 RED slice (#529). Refs #358.

Fixes per cluster (one commit each)

1. Typed intent authority (ADR-001, inventory row 1): 55d8d4c

reduce_group re-read IntuneIntent from every member's caller-writable named_data; the last writer won over the typed assignment field, so an inventory fact carrying IntuneIntent=notTargeted flipped a typed Required assignment into a NotTargeted transaction. Intent now travels as a typed field (StoreClassification.typed_intent / StoreObservation.typed_intent, set only by collect_from_assignment). The reducer reads intent exclusively from it, caller named_data is inert for intent, and two typed assignments stating different intents reduce to Unknown instead of letting member order pick a winner. The synthetic IntuneIntent named-data append was removed (nothing consumed it; the typed field is skip-serialized when absent, so the wire golden is unchanged).
Pinned by: typed_required_intent_survives_caller_writable_named_data (un-ignored).

2. Input-order terminal state (ADR-003, inventory row 2): 22b0bda

state_rank(candidate) >= state_rank(state) resolved equal-ranked terminal statements by input position: reversing the artifact vector flipped InstallCompleted into RegistrationFailure. State is now resolved over all state-bearing observations at once. The highest rank still wins. Within a rank, a record superseded by a later record from the same sequenced source artifact (event-log record ids, CCM record order) is dropped; that is the explicit source-native ordering that lets a linked retry land on its final outcome. Surviving contradictory states stay conservative at InsufficientEvidence (the module's existing no-single-conclusion vocabulary) rather than crowning an arbitrary winner. Supplied facts carry caller-chosen record numbers and are never sequenced against anything.
Pinned by: equivalent_input_permutation_does_not_change_the_reduction (un-ignored), plus the retry and ambiguous-success tests below.

3. app_id-only join (ADR-002, inventory row 3): f766353

joinable accepted a bare app_id match as a full join, so a package-identity-free installer failure merged into the product-identified transaction and became its terminal InstallerFailure at High confidence. An app id match now groups observations only while neither side claims a package correlation token. The rule is symmetric: identity-free records cannot enter a package-identified transaction, and package-identified records cannot donate identity to an identity-free group. Identity-free evidence sharing an app id still reduces, as its own Intune-app-level transaction that never names a package.
Pinned by: an_app_id_match_without_package_identity_cannot_drive_a_package_terminal_outcome (un-ignored). The Phase 2 semantics file now runs with zero ignored tests.

4. Order-dependent grouping (ADR-003): 1b00a92 (found by the pilot, not by Phase 2)

The adversarial permutation test exposed a fourth instability: greedy group discovery scanned observations in input order, and an observation bridging two groups (family-name token shared with one, product-id token with the other) left the second group as a separate transaction depending on discovery order. Permuting three artifacts turned one transaction into two. Grouping now processes observations in the canonical order of their observation ids (stable under permutation) and merges groups to a fixpoint under the same join rules observations use (shared token; shared app id only when both sides are token-free; conflicting identities/contexts/families never merge).
Pinned by: permuting_a_realistic_bundle_changes_no_conclusion.

5. Adversarial production hardening: a64b23d

  • Source classification (row 8): channel matching was case-insensitive substring, so a recognized provider next to Backup-...AppXDeploymentServer/... or Contoso-StoreAgent-Archive/Old classified as the OS channel. Channels now match an approved base exactly or with a / stream suffix only; prefixed, suffixed, archive, and unrelated channels stay Unknown. Pinned by unit tests in sources.rs and the integration test an_archive_suffixed_channel_never_becomes_store_evidence.
  • Evidence degradation (row 7): a known failure id logged at Information level was folded into unknown_version, conflating "unrecognized dialect" with "record contradicts its own level". A separate level_mismatch flag (skip-serialized when false; wire golden unchanged) now degrades confidence independently, with its own finding store-event-level-mismatch distinct from store-unknown-event-version. Pinned by a_known_failure_id_at_information_level_is_a_level_mismatch_not_an_unknown_version and unknown_version_and_level_mismatch_degrade_for_distinct_documented_reasons.
  • Installer-family isolation (row 5): the InstallerFailure terminal state had no finding rule at all: a Store-delivered Win32 installer failure produced findings silence while AppX failures were reported. New store-win32-installer-failed finding with Win32-native remediation, kept apart from the AppX registration rule. Pinned by mixed_appx_and_win32_observations_stay_separate_with_family_appropriate_findings.

Adversarial coverage added (05e0a85, all active and passing)

  • Terminal precedence/retries: a_success_linked_by_activity_id_replaces_the_earlier_failure (renamed and strengthened by the Hermes fixes below; formerly an_explicitly_ordered_retry_success_replaces_the_earlier_failure), a_source_ordered_late_failure_is_not_hidden_by_an_earlier_success, an_unlinked_success_fact_does_not_overwrite_a_failure_event (conservative InsufficientEvidence, Low confidence, both records cited, permutation-invariant).
  • Installer-family isolation: mixed AppX/Win32 observations for the same product stay two transactions with family-appropriate, family-scoped findings.
  • Source classification: archive-suffixed channel yields no observations, no transactions, coverage only; exact base without a stream suffix still matches; suffixed/prefixed/unrelated channels pinned Unknown (unit tests).
  • Evidence degradation: distinct flags, distinct findings, and the level is never promoted into an outcome.
  • Confidence (ADR-001): duplicating_device_only_evidence_cannot_raise_confidence (Medium stays Medium), duplicating_degraded_evidence_cannot_lift_the_degradation (six copies of an unassessable record: still Low, no terminal outcome), coverage_gaps_cannot_raise_confidence (gap reported, confidence unchanged).
  • Invariants (ADR-002/003): permutation, duplication, and irrelevant-evidence over a realistic assignment + IME + event bundle.
  • Redaction (ADR-004, scope PROVISIONAL, no implementation or token-API change): pins the observed equality scope. The token is a pure function of the masked value alone, i.e. global equality with no caller-controlled key, recorded but not endorsed for cross-artifact/session/export correlation. Also pinned: same value gives same token, distinct values give distinct tokens, restricted values are absent from the serialized export and from findings, the correlation grammar (product id) survives, and redacted_export_projection never alters reducer conclusions.

Hermes review fixes (e068cf6)

All four findings from the Hermes charter review comment, TDD (each landed as a RED test first):

  1. P1, typed event_version (ADR-001) — fixed. classify_event now consumes NormalizedWindowsEvent.event_version. A template revision beyond the baseline the rule table was written against (> 1) degrades to unknown_version coverage and returns before any signal is assigned: a known failure event id under an unsupported typed version can no longer classify as RegistrationFailed or drive any terminal outcome. This is deliberately stricter than the named Version payload check, whose keep-outcome-at-reduced-confidence behavior is a pinned fixture contract (unknown-event-version): there the template itself is understood and only a payload detail is newer; an unsupported template version means nothing extracted from the payload is assessable. Tests: an_unsupported_typed_event_version_cannot_drive_a_terminal_outcome (integration), an_unsupported_typed_event_version_is_surfaced_not_interpreted (rules unit, including versions 0 and 1 staying assessable).
  2. P1, retry linkage (ADR-003) — behavior changed; explicit linkage now required. The Store event grammar does carry an explicit operation-correlation token: the ETW activity id (NormalizedWindowsEvent.activity_id, documented as "correlates a multi-event operation"), which the reducer previously dropped. StoreObservation now carries it (skip-serialized when absent; wire golden unchanged), and supersedes requires a shared activity id before a later same-artifact success may replace a failure. Record order alone proves the success was written later, not that it retried this failure, so an unlinked or cross-activity later success leaves the contradiction alive and the reduction conservative (InsufficientEvidence, Low, both records cited). The gate is deliberately asymmetric and documented at the site: a later failure after a success is not the retry-success case ADR-003 restricts, and hiding it would suppress a device-reported failure, so source-native chronology still applies there. Tests: a_success_linked_by_activity_id_replaces_the_earlier_failure, a_later_unlinked_success_does_not_replace_a_same_artifact_failure (no ids and two different ids, both permuted).
  3. P2, tied provenance — fixed. The canonical state-candidate sort now tie-breaks (source_artifact_id, record_number) on the candidate's own error token, the only content that can still differ once the equal-states check passes, so duplicate same-record observations select the same error under any input permutation. Test: tied_provenance_duplicates_select_the_error_deterministically (pins the canonical winner, not just permutation equality).
  4. P2, coverage-finding normalization — fixed (accepted CodeRabbit finding). store-unknown-event-version and store-event-level-mismatch citations are sorted and de-duplicated (normalized_refs) before counting and emitting, so duplicated observations and artifact permutations cannot change the rendered count or citation order. Test: duplicated_degraded_observations_do_not_inflate_coverage_finding_citations.

Gates at e068cf6: cargo test -p cmtraceopen-parser 48 suites, 2151 passed, 0 failed, 0 ignored (semantics file: 22 passed); 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. No repo-wide rustfmt (pre-existing drift left untouched; only lines this change wrote were formatted).

Fixture expectations changed

None. All 17 fixture scenarios, the expected-full.json wire golden, and every prior unit test pass unchanged. The new wire fields (typedIntent, levelMismatch, and now activityId) are skip-serialized at their defaults precisely so no golden had to move without a semantic reason.

Gates (exact commands, run at head 05e0a85)

Gate Command Result
Tests cargo test -p cmtraceopen-parser 48 suites, 2146 passed, 0 failed, 0 ignored (semantics file: 18 passed, 0 ignored)
Clippy cargo clippy -p cmtraceopen-parser --all-targets -- -D warnings clean
Workspace cargo check --workspace clean
wasm32 cargo check --locked -p cmtraceopen-parser --target wasm32-unknown-unknown clean

No tauri::test::mock_app(); no rustfmt on pre-existing files.

Deliberately left for PR 4

  • No generic conformance layer, shared helper crate/module, or universal reducer: everything here is workload-local to microsoft_store. Candidate reusable shapes this pilot surfaced as evidence for PR 4: the conclusions-set permutation/duplication assertion helper, the state-candidate/source-native-supersession pattern, and the group merge-to-fixpoint.
  • The redaction token/equality scope contract (caller key, cross-artifact/session/export behavior) stays provisional per ADR-004; this PR only pins current behavior.
  • Explicit retry linkage for InstallerOutcomes (supplied facts have no source-native ordering; contradictions stay conservative).
  • Multiple distinct app ids inside one token-joined transaction still surface first-by-member-order; no realistic fixture or test exercises that conflict yet.

Stacked on #529 (base store/reducer-phase2-red). Refs #358.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved Microsoft Store app failure detection, including Win32 installer failures and event-level mismatches.
    • Corrected channel matching to exclude unrelated or archived event sources.
    • Improved correlation of transactions, retries, package identities, and installer families.
    • Reduced confidence when evidence is incomplete, contradictory, duplicated, or recorded at an unexpected severity.
  • Diagnostics
    • Added clearer installer-specific evidence and recommendations.
    • Unsupported event versions are reported without interpreting outcomes.
    • Improved consistency when processing reordered, duplicated, or unrelated event data.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The Microsoft Store parser now uses typed assignment intent, strict source matching, deterministic transaction reduction, confidence degradation for contradictory evidence, and findings for Win32 installer failures and event-level mismatches.

Changes

Microsoft Store semantics

Layer / File(s) Summary
Classification contracts and source matching
crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/{models,rules,sources}.rs
Classifications and observations carry typed intent and level-mismatch state. Unsupported event versions remain unknown. Channel matching accepts approved exact bases and slash-delimited stream suffixes.
Identity correlation and deterministic grouping
crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/reducer.rs
Grouping separates package identities from app IDs, carries activity IDs, processes observations in canonical order, and merges compatible groups.
State resolution and confidence propagation
crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/reducer.rs
State resolution uses source ordering, activity-linked retries, lifecycle ranking, deterministic contradictions, typed intent authority, and confidence caps for degraded evidence.
Findings and adversarial validation
crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/findings.rs, crates/cmtraceopen-parser/tests/*microsoft_store*, crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/microsoft-store/*
Findings cover installer failures and event-level mismatches. Tests and fixtures cover confidence, citations, serialization, grouping, retries, channel matching, duplication, permutation, and redaction.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant EventLog
  participant StoreSources
  participant StoreRules
  participant StoreReducer
  participant StoreFindings
  EventLog->>StoreSources: provide event channel and record
  StoreSources->>StoreRules: classify approved source
  StoreRules->>StoreReducer: provide typed intent, outcome, and mismatch state
  StoreReducer->>StoreFindings: provide transaction evidence and confidence
Loading

Possibly related PRs

Suggested labels: apps, test

🚥 Pre-merge checks | ✅ 4
✅ 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 follows Conventional Commits and accurately summarizes the Microsoft Store reducer changes under the intune scope.
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
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch store/reducer-phase3-fixes

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

@github-actions github-actions Bot added bug Something isn't working enhancement New feature or request intune Microsoft Intune related parser Log parser related labels Aug 8, 2026
@adamgell
adamgell marked this pull request as ready for review August 8, 2026 18:02
Copilot AI lite review requested due to automatic review settings August 8, 2026 18:02

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

This PR hardens the Intune Microsoft Store reducer semantics (Reducer Framework v1 pilot, Phase 3) by removing the Phase 2 order-dependent/authority bugs and adding adversarial, active test coverage to lock in the intended invariants for intent authority, state resolution, joins/grouping, evidence classification/degradation, confidence propagation, and redaction projection behavior.

Changes:

  • Makes typed assignment intent authoritative end-to-end (no intent read/write through caller-writable named_data), and resolves conflicting typed intents conservatively.
  • Removes input-order dependence in terminal state resolution and grouping by introducing source-native supersession (within sequenced artifacts) plus canonical grouping and merge-to-fixpoint.
  • Hardens event source classification and evidence degradation semantics, and extends findings to cover Win32 installer failure and event level mismatches; enables/extends the semantics test suite (no ignored tests).

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
crates/cmtraceopen-parser/tests/intune_windows_microsoft_store_semantics.rs Un-ignores Phase 2 RED tests and adds extensive adversarial/invariant coverage (retries, joins, confidence, redaction, etc.).
crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/sources.rs Replaces substring channel matching with exact base or /-suffix matching; adds unit tests for the stricter classifier.
crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/rules.rs Introduces typed_intent and separates level_mismatch from unknown_version during event classification; adds unit coverage.
crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/reducer.rs Implements typed-intent authority, app-id-only join restrictions, canonical grouping + fixpoint merge, and order-independent state resolution via source-native supersession.
crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/models.rs Extends StoreObservation serialization with typedIntent and levelMismatch (both skip-serialized by default).
crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/findings.rs Adds Win32 installer failure finding and event level mismatch finding; wires both into findings derivation.

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

@adamgell

adamgell commented Aug 8, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@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.

Actionable comments posted: 3

🤖 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.

Inline comments:
In
`@crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/findings.rs`:
- Around line 569-587: Normalize the evidence refs in the level-mismatch finding
flow before counting or passing them to push_finding: sort the refs and remove
duplicates. Apply the same normalization to push_unknown_event_version,
preserving the normalized order so rendered counts and citations are stable.

In
`@crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/reducer.rs`:
- Around line 475-502: The merge_groups_to_fixpoint implementation repeatedly
rescans all group pairs after each merge, producing cubic behavior for large
bundles. Replace the restart-from-zero strategy with an efficient merge process,
such as union-find over correlation tokens and app_ids or an incremental pair
scan that resumes from the affected group, while preserving the existing
field-resolution and member-aggregation semantics.
- Around line 743-756: Make the canonical ordering in the surviving-candidate
flow deterministic even when source_artifact_id and record_number tie. Extend
the sort key in surviving.sort_by_key with a stable tie-break derived from each
candidate’s data, including enough fields to distinguish same-artifact
observations and their errors, so the subsequent find_map error selection is
independent of caller-provided order.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 47c8e538-a8ed-411f-81c1-11f4213c7136

📥 Commits

Reviewing files that changed from the base of the PR and between 93dbb46 and 05e0a85.

📒 Files selected for processing (6)
  • crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/findings.rs
  • crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/models.rs
  • crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/reducer.rs
  • crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/rules.rs
  • crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/sources.rs
  • crates/cmtraceopen-parser/tests/intune_windows_microsoft_store_semantics.rs

Comment thread crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/findings.rs Outdated
Comment thread crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/reducer.rs Outdated
@adamgell

adamgell commented Aug 8, 2026

Copy link
Copy Markdown
Owner Author

Hermes charter review

Findings (most severe first)

P1 — Unsupported normalized event versions can still drive terminal outcomes

Location: crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/rules.rs:515-535

classify_event checks only named_data["Version"]; it ignores the existing normalized event_version: Option<u32>. A recognized AppX provider/channel with known failure event 404, event_version = Some(2), no named Version, and a valid package identity is therefore classified as RegistrationFailed rather than unknown/degraded evidence. That can produce a terminal finding from an unsupported version.

Contract: violates the unsupported-version/coverage rule and ADR-001's prohibition on non-assessable evidence driving terminal conclusions.

Disposition: OPEN / merge-blocking. Add a regression using NormalizedWindowsEvent.event_version and make unsupported versions remain conservative.

P1 — Record order is treated as retry linkage without an explicit session/activity link

Location: crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/reducer.rs:694-707, 738-756

supersedes treats any later record number in the same sequenced artifact as superseding an earlier record. It does not require activity_id, transaction/session identity, or an explicit retry marker. A failure at record 10 from one deployment attempt followed by an unrelated success at record 11 for the same package can therefore reduce to InstallCompleted.

Contract: ADR-003 permits source-native chronology, but separately requires explicit retry linkage before a retry success replaces a failure. Chronology alone is not linkage.

Disposition: OPEN / merge-blocking unless the Store source contract explicitly proves artifact-local record order is sufficient retry linkage. Otherwise preserve the contradiction conservatively and add a cross-session/activity adversarial test.

P2 — Equal provenance keys leave error selection input-order dependent

Location: crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/reducer.rs:743-755

The canonical sort key is only (source_artifact_id, record_number). Candidates with the same keys but different errors remain tied; find_map then selects whichever candidate was retained first, so duplicate/same-record observations can change the reported error under input permutation.

Disposition: OPEN. Add a deterministic tie-break derived from candidate data and a regression. This is the valid portion of CodeRabbit's tie-break comment; it is non-blocking only if duplicate record IDs are explicitly impossible by the source contract.

P2 — Coverage findings are not normalized

Location: crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/findings.rs:535-590

store-unknown-event-version and store-event-level-mismatch count and emit raw observation references without sorting/deduplicating. Duplicate observations or artifact permutations can change citation counts/order even though the reducer conclusion is unchanged.

Contract: findings remain evidence-backed, but deterministic evidence projection and duplicate invariance are incomplete.

Disposition: ACCEPT CodeRabbit finding. Normalize refs before counting and emitting; add a focused regression.

Three-layer contract assessment

  • Evidence strength vs confidence: FAIL for the normalized event_version gap above; otherwise the active tests cover weak/unknown evidence and confidence caps.
  • Identity/correlation strength: PARTIAL/PASS for app-id-only package joins and installer-family isolation; the changed tests cover these. Retry linkage remains unresolved.
  • Chronology/terminal precedence: FAIL for the retry-linkage concern and tied error selection; input-order tests pass for the covered cases.
  • Coverage honesty: PARTIAL; archive channels, missing artifacts, unknown event IDs, and level mismatches are surfaced, but unsupported normalized versions can still become outcomes.
  • Redaction scope: PASS for current projection behavior / PROVISIONAL by ADR-004; tests preserve conclusions and remove restricted values, but the test documents rather than resolves the architecture's global token equality scope.
  • Adversarial coverage: GOOD but incomplete for permutations, duplicates, irrelevant packages, family isolation, unknown/contradictory evidence, and redaction; missing normalized event-version, explicit retry/session linkage, tied same-record candidates, and normalized finding refs.

CodeRabbit dispositions

  • Accepted: normalize unknown-version and level-mismatch evidence references.
  • Accepted with semantic concern: deterministic tie-break for equal (artifact, record) candidates.
  • Rejected as a merge gate: cubic merge_groups_to_fixpoint complexity. The observation may matter for future bounded-performance work, but no bundle-size/performance contract or demonstrated failure was supplied; it is not a charter semantic blocker for this PR.

Named gates

  • CI: NOT OBSERVED / NOT GREEN AS A FULL GATE. GitHub currently exposes only the label check and the CodeRabbit status context; no full build/test/clippy/TS matrix is present in the PR checks observed.
  • CodeRabbit approved_at_head: NO. The review is COMMENTED at head 05e0a851c1aa2b5ce1853a8610e478d6ef3624aa; the CodeRabbit status check is SUCCESS, which is not an approval state.
  • Contract conformance: FAIL / OPEN. The P1 findings above survive verification against the exact head, so the contract gate is not satisfied.
  • Local verification: focused Store semantics test target: 18 passed; git diff --check: passed. cargo fmt --all -- --check: failed on changed reducer/test formatting and pre-existing/unrelated repository formatting. Full parser, strict Clippy, wasm32, TypeScript, and native Windows validation were not established by the observed PR gates.

Coverage statement

Reviewed the exact six-file diff origin/store/reducer-phase2-red...origin/store/reducer-phase3-fixes, the head snapshots via git show, and the charter load order: routing indexes, specialist soul/memory, ADR-001..004, reducer plan/checklist, contract/adversary/integration charters, AGENTS.md, and CLAUDE.md. Applied contract, adversarial, and mechanical review layers. No files were changed, no threads were resolved, and no merge was performed. This review did not perform native Windows/Intune acceptance, wasm32 validation, full workspace CI, or production-corpus validation.

Overall disposition: hold for the two P1 semantic findings; the named gates are not all satisfied.

@adamgell

adamgell commented Aug 8, 2026

Copy link
Copy Markdown
Owner Author

Re: Hermes charter review — dispositions at e068cf6

All four findings are addressed, each RED-test-first. Summary of dispositions:

P1 — typed event_version: FIXED

classify_event now consumes NormalizedWindowsEvent.event_version. An unsupported template revision (> 1; 0 and 1 are the baseline the event-id table was written against) sets unknown_version and returns before any signal is assigned, so the review's exact scenario (known failure id 404, event_version: Some(2), no named Version, valid identity) now reduces to InsufficientEvidence at Low confidence with a store-unknown-event-version citation, never RegistrationFailed and never a terminal finding. The named Version payload path keeps its pinned fixture contract (unknown-event-version: outcome kept at reduced confidence) and the distinction is documented at the site: an unsupported template version means nothing extracted from the payload is assessable; an unsupported payload-detail version arrives inside a template we do understand.
Tests: an_unsupported_typed_event_version_cannot_drive_a_terminal_outcome, an_unsupported_typed_event_version_is_surfaced_not_interpreted.

P1 — retry linkage: BEHAVIOR CHANGED, explicit linkage now required

Path taken: an explicit linkage key exists in the source grammar, so it is now required. The Windows event grammar carries the ETW activity id (NormalizedWindowsEvent.activity_id, documented in normalized.rs as "correlates a multi-event operation"); the reducer was dropping it. StoreObservation now retains it (skip-serialized when absent, wire golden unchanged) and supersedes requires a shared activity id before a later same-artifact success may replace a failure. Without that link — no ids, or two different ids — the contradiction stays alive and the reduction stays conservative (InsufficientEvidence, Low, both records cited), exactly the chronology-vs-linkage separation ADR-003 draws. The gate is deliberately asymmetric and documented at the site: a later failure after a success is not the retry-success case ADR-003 restricts, and hiding it would suppress a device-reported failure, so plain source-native chronology still governs that direction. IME text and supplied facts carry no correlation token, so no failure-to-success supersession is possible from them at all.
Tests: a_success_linked_by_activity_id_replaces_the_earlier_failure (permuted), a_later_unlinked_success_does_not_replace_a_same_artifact_failure (adversarial: two attempts, no explicit link, and cross-activity, both permuted).

P2 — tied provenance error selection: FIXED

The canonical sort in resolve_state now tie-breaks (source_artifact_id, record_number) on the candidate's own error token — the only candidate content that can still differ once the equal-states check passes — so duplicate same-record observations select the same error under any input permutation, and the winner is canonical rather than first-seen.
Test: tied_provenance_duplicates_select_the_error_deterministically.

P2 — coverage-finding normalization: FIXED (accepted CodeRabbit finding)

store-unknown-event-version and store-event-level-mismatch citations pass through normalized_refs (sort + dedupe) before counting and emitting, so duplicated observations and artifact permutations cannot change the rendered count or citation order.
Test: duplicated_degraded_observations_do_not_inflate_coverage_finding_citations.

Gates at e068cf6

  • cargo test -p cmtraceopen-parser: 48 suites, 2151 passed, 0 failed, 0 ignored (semantics file: 22 passed, 0 ignored)
  • 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
  • No repo-wide rustfmt; pre-existing drift untouched, lines written by this change are rustfmt-clean.

CodeRabbit's merge_groups_to_fixpoint complexity comment remains not actioned per the review's own disposition (no performance contract or demonstrated failure); it stays a candidate for future bounded-performance work.

@adamgell
adamgell deleted the branch main August 8, 2026 21:59
@adamgell adamgell closed this Aug 8, 2026
@adamgell adamgell reopened this Aug 8, 2026
@adamgell
adamgell changed the base branch from store/reducer-phase2-red to main August 8, 2026 22:00
adamgell and others added 7 commits August 8, 2026 18:00
…med_data

Reducer Framework v1 Store pilot Phase 3, cluster 1 (typed intent
authority; ADR-001, inventory row 1, refs #358).

reduce_group previously re-read "IntuneIntent" from every group
member's caller-writable named_data, so the last writer won over the
typed StoreAssignment.intent field: a package inventory fact carrying
named_data IntuneIntent=notTargeted flipped a typed Required assignment
into a NotTargeted transaction.

The typed assignment intent now travels as a typed field:

- StoreClassification and StoreObservation gain typed_intent, set only
  by collect_from_assignment from the typed StoreAssignment.intent.
  The field is skip-serialized when absent, so the wire shape of
  non-assignment observations (and the expected-full.json golden) is
  unchanged.
- reduce_group folds intent exclusively from typed_intent. Two typed
  assignments stating different intents are an unresolved contradiction
  and reduce to Unknown instead of letting member order pick a winner.
- The reducer no longer writes a synthetic "IntuneIntent" pair into
  the assignment observation's named_data; caller named_data passes
  through untouched and is inert for intent.

Un-ignores typed_required_intent_survives_caller_writable_named_data.
No fixture expectation changed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reducer Framework v1 Store pilot Phase 3, cluster 2 (input order and
chronology; ADR-003, inventory row 2, refs #358).

reduce_group folded transaction state online with
state_rank(candidate) >= state_rank(state), so equal-ranked terminal
statements were resolved by whichever record the caller supplied last:
reversing the artifact vector flipped InstallCompleted into
RegistrationFailure with no change in evidence.

State is now resolved over all state-bearing observations at once:

- the highest lifecycle rank still wins;
- within that rank, a record superseded by a later record from the
  same sequenced source artifact (event-log record ids, CCM record
  order) is dropped, which is the explicit source-native ordering that
  lets a linked retry land on its final outcome;
- if the surviving records still state more than one distinct state,
  the contradiction is unresolved and the reduction stays conservative
  at InsufficientEvidence (the module's existing no-single-conclusion
  state) instead of crowning an arbitrary winner. Supplied facts carry
  caller-chosen record numbers and are never sequenced against
  anything.

Un-ignores equivalent_input_permutation_does_not_change_the_reduction.
No fixture expectation changed: no fixture encodes an equal-ranked
cross-artifact contradiction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e transactions

Reducer Framework v1 Store pilot Phase 3, cluster 3 (identity and
correlation; ADR-002, inventory row 3, refs #358).

joinable() accepted a bare Intune app id match as a full join, so an
installer failure that named no package at all was merged into the
product-identified transaction that shared its app id, and its
Win32InstallerFailed signal became that package's terminal
InstallerFailure at High confidence.

An app id match now groups observations only while neither the
observation nor the group claims any package correlation token. The
rule is symmetric: an identity-free record cannot be merged into a
package-identified transaction, and a package-identified record cannot
donate its identity to a group built from identity-free evidence.
Identity-free evidence sharing an app id still reduces - as its own
Intune-app-level transaction that never names a package.

Un-ignores
an_app_id_match_without_package_identity_cannot_drive_a_package_terminal_outcome;
the Phase 2 semantics file now runs with zero ignored tests. No fixture
expectation changed: every fixture observation carrying an app id also
carries a package token.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… and Win32 failure findings

Reducer Framework v1 Store pilot Phase 3, adversarial production
hardening for three inventory clusters (refs #358):

Source classification (inventory row 8): classify_event_source matched
channels by case-insensitive substring, so an exact recognized provider
name paired with 'Backup-...AppXDeploymentServer/...' or
'Contoso-StoreAgent-Archive/Old' was classified as the OS channel.
Channels now match only an approved base exactly or with a '/' stream
suffix; prefixed, suffixed, archive, and unrelated channels stay
Unknown. Unit tests pin both directions.

Evidence degradation (inventory row 7): a known failure event id logged
at Information level was folded into unknown_version, conflating 'this
build does not recognize the dialect' with 'the record contradicts its
own level'. StoreClassification/StoreObservation now carry a separate
level_mismatch flag (skip-serialized when false, so the wire golden is
unchanged), both flags independently cap transaction confidence at Low,
and a new store-event-level-mismatch finding documents the second
reason distinctly from store-unknown-event-version.

Installer-family isolation (inventory row 5): the InstallerFailure
terminal state had no finding rule at all, so a Store-delivered Win32
installer failure produced findings silence while AppX failures were
reported. New store-win32-installer-failed finding with Win32-native
remediation, kept apart from the AppX registration rule.

No fixture expectation changed.

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

Reducer Framework v1 Store pilot Phase 3, cluster 2 follow-up
(ADR-003, refs #358). Found by the Phase 3 adversarial permutation
test (next commit), not by the Phase 2 RED set.

Greedy group discovery was order-dependent in two ways:

- observations were scanned in input order, so which group was
  discovered first depended on the artifact vector;
- an observation bridging two groups (sharing a family-name token with
  one and a product-id token with the other) was placed into whichever
  group matched first, and the other group stayed a separate
  transaction. Reordering the same three artifacts turned one
  transaction into two, with the bridge's Intune-intent evidence
  stranded on the second.

Grouping now processes observations in the canonical order of their
observation ids (identifiers of the evidence itself, stable under
input permutation) and then merges groups to a fixpoint under the same
join rules observations use: shared token merges; shared app id merges
only while both sides are package-identity-free; conflicting
identities, contexts, and installer families never merge. Transaction
ids and cited evidence remain in source order.

No fixture expectation changed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reducer Framework v1 Store pilot Phase 3, adversarial pilot (refs
#358). Active, passing tests over the fixed reducer for every
remaining inventory cluster, exercised through the public
analyze_store_bundle API:

- terminal precedence/retries (ADR-003): a retry ordered by the same
  sequenced source's record numbers transitions failure->success (and
  the mirror image keeps a late failure); an inventory success fact
  that cannot be ordered against a failure event stays a conservative
  InsufficientEvidence contradiction at Low confidence with both
  records cited, under both input orders.
- installer-family isolation: AppX and Store-Win32 failures for the
  same product stay two transactions; store-registration-failed cites
  only AppX evidence and store-win32-installer-failed cites only
  installer-native evidence.
- source classification: an archive-suffixed channel with a
  recognized provider yields no observations, no transactions, and no
  terminal outcome - coverage only (exact/prefix matching itself is
  unit-tested in sources.rs).
- evidence degradation: unknown dialect and known-event level
  mismatch each degrade to Low via their own flag and their own
  finding (store-unknown-event-version vs store-event-level-mismatch),
  and the level is never promoted into an outcome.
- confidence (ADR-001): duplicating device-only evidence cannot cross
  Medium; six copies of an unassessable record are still Low and
  produce no terminal outcome; a coverage gap never raises confidence
  and is itself reported.
- permutation/duplication/irrelevant evidence (ADR-002/003): over a
  realistic assignment+IME+event bundle, permuting artifacts changes
  no conclusion (this test flushed out the grouping fix in the
  previous commit), duplicating an artifact changes no conclusion, and
  an unrelated package's failure cannot alter another transaction.
- redaction (ADR-004, provisional): pins the currently observed
  equality scope - the token is a pure function of the value alone,
  i.e. global equality with no caller key, recorded but not endorsed -
  plus same-value/same-token, distinct-value/distinct-token,
  restricted values absent from the export and findings, correlation
  grammar surviving, and redaction never altering reducer conclusions.
  No token API was added or changed.

Also documents the Phase 3 status in the test file header; the Phase 2
RED recordings remain as history.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- classify_event consumes the typed event_version: an unsupported
  template revision degrades to unknown-version coverage and cannot
  drive a terminal outcome (ADR-001)
- a later success supersedes a failure only when the ETW activity id
  links both records to one operation; unlinked contradictions stay
  conservative (ADR-003 separates chronology from retry linkage)
- state-candidate ordering breaks (artifact, record) ties on the error
  token so duplicate observations cannot make error selection depend on
  caller order
- the unknown-version and level-mismatch findings normalize (sort and
  dedupe) their citations before counting or emitting

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@adamgell
adamgell force-pushed the store/reducer-phase3-fixes branch from e068cf6 to 4b9af95 Compare August 8, 2026 22:01
@coderabbitai coderabbitai Bot added the test Testing related label Aug 8, 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.

Actionable comments posted: 3

🤖 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.

Inline comments:
In
`@crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/findings.rs`:
- Around line 268-285: Update all six failure findings in the relevant reducer
to derive confidence from the weakest transaction in their affected set instead
of hard-coding IntuneFindingConfidence::High. Reuse the reducer’s existing
confidence-capping logic for unknown versions, level mismatches, and malformed
contributors, and add coverage verifying degraded confidence propagates to each
failure rule.

In
`@crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/reducer.rs`:
- Around line 917-921: The level-mismatch degradation is computed but not
exposed consistently. In
crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/reducer.rs
lines 917-921, add the value computed at line 868 to StoreTransaction alongside
unknown_version_observed; in
crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/models.rs
lines 455-461, give level_mismatch the same always-present serialization
behavior as unknown_version.

In `@crates/cmtraceopen-parser/tests/intune_windows_microsoft_store_semantics.rs`:
- Around line 1370-1385: Update duplicating_an_artifact_changes_no_conclusion so
the copied artifact’s events are rebuilt with contexts using the new artifact
ID, rather than cloning events that still reference "appx". Preserve the copied
artifact’s distinct artifact_id and ensure its observations receive the
corresponding source_artifact_id and evidence identifiers, exercising the
intended cross-artifact reduction path.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ef020705-eceb-4be8-863b-8720da7d6436

📥 Commits

Reviewing files that changed from the base of the PR and between 05e0a85 and 4b9af95.

📒 Files selected for processing (5)
  • crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/findings.rs
  • crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/models.rs
  • crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/reducer.rs
  • crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/rules.rs
  • crates/cmtraceopen-parser/tests/intune_windows_microsoft_store_semantics.rs

Three review threads, all verified against the code and fixed:

- Outcome findings no longer overstate degraded evidence. Every rule that
  asserts a transaction outcome (the six failure rules, no-interactive-user,
  and the two completion rules) now inherits the weakest confidence among
  its affected transactions instead of hard-coding High, so a transaction
  the reducer capped at Low for an unknown dialect, a level mismatch, or a
  malformed contributor caps every finding built on it (ADR-001). Gap and
  attribution rules keep their own deliberate confidence: their claim is
  about what is missing, not about an outcome.

- The two degradation causes stay distinguishable on the wire.
  StoreTransaction now carries levelMismatchObserved beside
  unknownVersionObserved, and StoreObservation serializes levelMismatch
  unconditionally exactly like unknownVersion, so a consumer reading a Low
  transaction can tell which cause fired without replaying observations.

- duplicating_an_artifact_changes_no_conclusion now rebuilds the copied
  artifact's events under the new artifact id, giving the copy its own
  source_artifact_id and evidence ids and actually exercising the
  cross-artifact reduction path the test claims to cover.

Fixtures updated accordingly: every transaction states
levelMismatchObserved explicitly (pinned in the fixture contract), the
three outcome findings over degraded or one-sided transactions now state
their honest confidence, and the redacted-export golden was regenerated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai coderabbitai Bot added the apps App management related label Aug 8, 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.

Actionable comments posted: 1

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/src/intune/apps/windows/microsoft_store/models.rs (1)

437-462: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Publish these public API changes as a breaking release.

StoreObservation and StoreTransaction are public types. Their new fields break downstream struct literals. The fields also change the serialized shape while MICROSOFT_STORE_SCHEMA_VERSION remains 1.

Publish this as 0.2.0 or later, increment the schema version, or expose the fields through a versioned API.

🤖 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/src/intune/apps/windows/microsoft_store/models.rs`
around lines 437 - 462, Publish the new public fields in StoreObservation
(models.rs:437-462) and StoreTransaction (models.rs:492-498) as a breaking API
change: bump the crate version to 0.2.0 or later and increment
MICROSOFT_STORE_SCHEMA_VERSION to reflect the serialized shape change, or expose
these fields through a versioned API instead. Apply the chosen compatibility
strategy to both affected types.

Source: Path instructions

🤖 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.

Inline comments:
In `@crates/cmtraceopen-parser/tests/intune_windows_microsoft_store.rs`:
- Line 224: Add "levelMismatchObserved" to the serialization key list used by
the generic stability test, alongside the existing keys. Update the key-check
list rather than changing assert_transactions, ensuring the new field’s wire
name is validated.

---

Outside diff comments:
In `@crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/models.rs`:
- Around line 437-462: Publish the new public fields in StoreObservation
(models.rs:437-462) and StoreTransaction (models.rs:492-498) as a breaking API
change: bump the crate version to 0.2.0 or later and increment
MICROSOFT_STORE_SCHEMA_VERSION to reflect the serialized shape change, or expose
these fields through a versioned API instead. Apply the chosen compatibility
strategy to both affected types.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5c598ca0-a8d3-4ed9-9363-ea58fece8abc

📥 Commits

Reviewing files that changed from the base of the PR and between 4b9af95 and 4d3dd40.

📒 Files selected for processing (22)
  • crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/findings.rs
  • crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/models.rs
  • crates/cmtraceopen-parser/src/intune/apps/windows/microsoft_store/reducer.rs
  • crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/microsoft-store/acquisition-license-failure/expected.json
  • crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/microsoft-store/deployment-registration-failure/expected.json
  • crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/microsoft-store/download-staging-failure/expected.json
  • crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/microsoft-store/incomplete-event-channel-coverage/expected.json
  • crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/microsoft-store/intune-intent-without-os-event/expected.json
  • crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/microsoft-store/malformed-export-and-redaction/expected.json
  • crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/microsoft-store/no-interactive-user/expected.json
  • crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/microsoft-store/os-error-without-intune-intent/expected.json
  • crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/microsoft-store/provisioned-package-install-success/expected.json
  • crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/microsoft-store/provisioning-failure/expected.json
  • crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/microsoft-store/same-display-name-different-package-family/expected.json
  • crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/microsoft-store/store-win32-handoff-success/expected.json
  • crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/microsoft-store/uninstall-failure/expected.json
  • crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/microsoft-store/uninstall-success/expected.json
  • crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/microsoft-store/unknown-event-version/expected.json
  • crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/microsoft-store/user-context-uwp-install-success/expected-full.json
  • crates/cmtraceopen-parser/tests/fixtures/intune/apps/windows/microsoft-store/user-context-uwp-install-success/expected.json
  • crates/cmtraceopen-parser/tests/intune_windows_microsoft_store.rs
  • crates/cmtraceopen-parser/tests/intune_windows_microsoft_store_semantics.rs

Comment thread crates/cmtraceopen-parser/tests/intune_windows_microsoft_store.rs
@adamgell

adamgell commented Aug 8, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai approve

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
✅ Action performed

Comments resolved and changes approved.

@adamgell
adamgell merged commit edbbb65 into main Aug 8, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

apps App management related bug Something isn't working enhancement New feature or request intune Microsoft Intune related parser Log parser related test Testing related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants