Skip to content

feat(jamf): macOS JAMF workspace - #315

Merged
adamgell merged 44 commits into
mainfrom
feat/jamf-workspace
Jul 31, 2026
Merged

feat(jamf): macOS JAMF workspace#315
adamgell merged 44 commits into
mainfrom
feat/jamf-workspace

Conversation

@adamgell

@adamgell adamgell commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Draft. Tracking: #314

Adds a macOS JAMF Pro workspace — environment detection, three structured log parsers, known sources, collector manifest family, and a six-tab frontend workspace. Opened as a draft because this is 28 commits and 46 files written three months ago, now rebased onto current main, and it has never been reviewed or exercised end to end.

Contents

Backend (src-tauri/src/jamf/)

  • Environment detection: JAMF binary presence, JSS URL, JAMF Connect
  • Path constants and home-dir helpers, consolidated plist paths
  • Structured parsers for the policy log, Self Service usage log, and JAMF Connect log, with real-data fixtures and 5 integration test files
  • Known-source entries for JAMF Pro and JAMF Connect
  • macos-jamf family in the collector bundle manifest
  • Wired into the IPC handler; macos-jamf exposed in the backend workspace allowlist

Frontend (src/workspaces/macos-jamf/)

  • Zustand store with tests, TS types, macos-jamf WorkspaceId, registry registration
  • Tabs: Overview, Logs, Policies, Profiles, Self Service, JAMF Connect
  • Extracts ProfileDrilldown out of macos-diag for reuse, and removes dead code from MacosDiagProfilesTab

macOS release signing — independent of JAMF, and a candidate to split out into its own PR: Developer ID signing + entitlements + build script, an .env.example template with .env.local auto-sourcing, updater-key-missing downgraded to a warning, and a docs fix noting --no-bundle is unsupported.

Rebase notes

Replayed 27 commits onto main from 138 behind. One conflict, in crates/cmtraceopen-parser/src/collector/profile.rs, where main and the branch had each appended a different #[test] at the same location — both kept.

The rebase surfaced a real integration gap worth a reviewer's eye: main has since added every_backend_available_workspace_has_a_menu_descriptor, which this branch could not have satisfied. Exposing macos-jamf in the backend allowlist without a WORKSPACE_DESCRIPTORS entry failed that test. Registered under Endpoint Management next to Intune, gated to macOS to match the workspace's own platforms: ["macos"] — please sanity-check that grouping choice, since macos-diag sits under System & Security instead.

Verification

  • cargo test — 427 lib tests + all integration suites green
  • cargo clippy --all-targets -- -D warnings — clean
  • npx tsc --noEmit — clean
  • npm test — 529/529 across 41 files

Not verified

  • No e2e coverage. None of the 4 Playwright specs touch JAMF, so CI's E2E job will pass without exercising any of this. Tracked in Track: macOS JAMF workspace — rebase landed, e2e coverage outstanding #314.
  • Never run against a JAMF-managed Mac. The parsers are fixture-tested only. The parsers have now been exercised against logs captured from a live JAMF-managed Mac (2,730-line jamf.log, Self Service logs, a 32-profile system_profiler capture) via the env-gated tests/jamf_real_fixtures.rs suite; the capture stays private, so CI skips it. This surfaced and fixed a real defect: the Self Service parser expected a log format that does not exist and parsed zero events from a real log.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a macOS JAMF workspace for reviewing JAMF Pro and JAMF Connect environments.
    • View environment details, policy activity, Self Service events, Connect events, deployed profiles, and available log files.
    • Added filtering for JAMF-related profiles and detailed profile inspection.
    • Added support for collecting and parsing JAMF logs, including file sizes and scan summaries.
    • Improved profile detail presentation in the macOS diagnostics workspace.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a new macOS JAMF Pro workspace to CMTrace Open (Tauri backend + React frontend), including backend JAMF environment detection/parsing/known-sources plumbing and a new six-tab UI workspace. It also refactors macos-diag to extract a reusable ProfileDrilldown component.

Changes:

  • Introduces a new macos-jamf workspace (frontend registry entry, WorkspaceId, Zustand store, and tabbed UI).
  • Adds a new Rust jamf module with environment detection plus parsers for policy log, Self Service log, and JAMF Connect log; wires new IPC commands and adds integration tests/fixtures.
  • Adds JAMF known-source metadata and collector profile entries, and registers macos-jamf in the backend menu descriptors / available-workspaces allowlist.

Reviewed changes

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

Show a summary per file
File Description
src/workspaces/registry.ts Registers the new macos-jamf workspace in the frontend workspace list.
src/workspaces/macos-jamf/types.ts Defines JAMF workspace types (environment, events, tab IDs).
src/workspaces/macos-jamf/MacosJamfWorkspace.tsx Top-level JAMF workspace component; loads environment and renders active tab.
src/workspaces/macos-jamf/MacosJamfTabStrip.tsx Tab UI for navigating JAMF workspace sections.
src/workspaces/macos-jamf/MacosJamfSelfServiceTab.tsx Frontend for parsing/displaying Self Service usage events via IPC.
src/workspaces/macos-jamf/MacosJamfProfilesTab.tsx Frontend for filtering/displaying JAMF-related profiles and drilldown.
src/workspaces/macos-jamf/MacosJamfPoliciesTab.tsx Frontend for parsing/displaying jamf.log policy events.
src/workspaces/macos-jamf/MacosJamfOverviewTab.tsx Overview cards for detected JAMF/JAMF Connect environment state.
src/workspaces/macos-jamf/MacosJamfLogsTab.tsx Scans JAMF-related log locations and lists discovered files.
src/workspaces/macos-jamf/MacosJamfEnvironmentBanner.tsx Banner summarizing whether JAMF is detected.
src/workspaces/macos-jamf/MacosJamfConnectTab.tsx Frontend for parsing/displaying JAMF Connect events via IPC.
src/workspaces/macos-jamf/jamf-store.ts Zustand store for JAMF workspace state + slice load helpers.
src/workspaces/macos-jamf/jamf-store.test.ts Unit tests for the JAMF Zustand store transitions.
src/workspaces/macos-jamf/index.ts Defines the workspace definition (lazy-loaded component/sidebar, platform gating).
src/workspaces/macos-diag/ProfileDrilldown.tsx Extracted reusable profile payload drilldown UI.
src/workspaces/macos-diag/MacosDiagProfilesTab.tsx Refactors macOS diagnostics profiles tab to use ProfileDrilldown.
src/workspaces/macos-diag/index.ts Re-exports ProfileDrilldown for cross-workspace reuse.
src/types/log.ts Adds macos-jamf to WorkspaceId.
src-tauri/src/menu.rs Adds macos-jamf descriptor and updates menu grouping test expectations.
src-tauri/src/lib.rs Exposes commands publicly; wires JAMF IPC handlers; exports jamf module behind feature gate.
src-tauri/src/commands/mod.rs Adds commands::jamf module (feature-gated).
src-tauri/src/commands/known_sources.rs Adds macOS JAMF/JAMF Connect known log sources metadata.
src-tauri/src/commands/jamf.rs Implements JAMF IPC commands (collect env, parse logs, scan logs, filter profiles).
src-tauri/src/commands/app_config.rs Exposes macos-jamf in backend available-workspaces allowlist.
src-tauri/src/jamf/mod.rs JAMF backend module root.
src-tauri/src/jamf/models.rs JAMF backend models used as IPC return types.
src-tauri/src/jamf/paths.rs Centralized JAMF-related path constants and helpers.
src-tauri/src/jamf/detect.rs Collects JAMF environment signals (binary, version, JSS URL, Connect, directories).
src-tauri/src/jamf/policy_log.rs Parser for jamf.log into structured policy events (with year inference).
src-tauri/src/jamf/self_service.rs Parser for selfservice.log into structured usage events.
src-tauri/src/jamf/connect.rs Parser for JAMFConnect.log into structured Connect events.
src-tauri/src/jamf/profiles.rs Filters macOS profiles to likely JAMF-deployed profiles.
src-tauri/tests/jamf_environment.rs Integration tests for environment collection behavior.
src-tauri/tests/jamf_policy_log_parsing.rs Integration tests for jamf.log parsing.
src-tauri/tests/jamf_self_service_log_parsing.rs Integration tests for selfservice.log parsing.
src-tauri/tests/jamf_connect_log_parsing.rs Integration tests for JAMFConnect.log parsing.
src-tauri/tests/jamf_known_sources.rs Ensures JAMF known sources exist and are platform-gated.
src-tauri/tests/fixtures/jamf_policy_log_basic.log Test fixture for policy log parsing.
src-tauri/tests/fixtures/jamf_policy_log_errors.log Test fixture for policy log error parsing.
src-tauri/tests/fixtures/jamf_self_service_basic.log Test fixture for self service parsing.
src-tauri/tests/fixtures/jamf_connect_basic.log Test fixture for connect log parsing.
crates/cmtraceopen-parser/src/collector/profile.rs Adds a test ensuring JAMF collector family filtering works.
crates/cmtraceopen-parser/src/collector/profile_data.json Adds macos-jamf collector log/command items.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/workspaces/macos-jamf/MacosJamfProfilesTab.tsx Outdated
Comment thread src-tauri/src/jamf/connect.rs
@adamgell

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@adamgell, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 27 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fa058b37-db46-4c41-bb2c-cd91fed4d24e

📥 Commits

Reviewing files that changed from the base of the PR and between b2b6286 and d2ab9c8.

⛔ Files ignored due to path filters (4)
  • src-tauri/tests/fixtures/jamf_connect_basic.log is excluded by !**/*.log
  • src-tauri/tests/fixtures/jamf_policy_log_basic.log is excluded by !**/*.log
  • src-tauri/tests/fixtures/jamf_policy_log_errors.log is excluded by !**/*.log
  • src-tauri/tests/fixtures/jamf_self_service_basic.log is excluded by !**/*.log
📒 Files selected for processing (46)
  • crates/cmtraceopen-parser/src/collector/profile.rs
  • crates/cmtraceopen-parser/src/collector/profile_data.json
  • src-tauri/src/commands/app_config.rs
  • src-tauri/src/commands/jamf.rs
  • src-tauri/src/commands/known_sources.rs
  • src-tauri/src/commands/mod.rs
  • src-tauri/src/jamf/connect.rs
  • src-tauri/src/jamf/detect.rs
  • src-tauri/src/jamf/mod.rs
  • src-tauri/src/jamf/models.rs
  • src-tauri/src/jamf/paths.rs
  • src-tauri/src/jamf/policy_log.rs
  • src-tauri/src/jamf/profiles.rs
  • src-tauri/src/jamf/self_service.rs
  • src-tauri/src/jamf/text.rs
  • src-tauri/src/jamf/time.rs
  • src-tauri/src/lib.rs
  • src-tauri/src/macos_diag/environment.rs
  • src-tauri/src/macos_diag/profiles.rs
  • src-tauri/src/menu.rs
  • src-tauri/tests/jamf_connect_log_parsing.rs
  • src-tauri/tests/jamf_environment.rs
  • src-tauri/tests/jamf_ipc_contract.rs
  • src-tauri/tests/jamf_known_sources.rs
  • src-tauri/tests/jamf_parser_robustness.rs
  • src-tauri/tests/jamf_policy_log_parsing.rs
  • src-tauri/tests/jamf_real_fixtures.rs
  • src-tauri/tests/jamf_self_service_log_parsing.rs
  • src/types/log.ts
  • src/workspaces/macos-diag/MacosDiagProfilesTab.tsx
  • src/workspaces/macos-diag/ProfileDrilldown.tsx
  • src/workspaces/macos-diag/index.ts
  • src/workspaces/macos-jamf/MacosJamfConnectTab.tsx
  • src/workspaces/macos-jamf/MacosJamfEnvironmentBanner.tsx
  • src/workspaces/macos-jamf/MacosJamfLogsTab.tsx
  • src/workspaces/macos-jamf/MacosJamfOverviewTab.tsx
  • src/workspaces/macos-jamf/MacosJamfPoliciesTab.tsx
  • src/workspaces/macos-jamf/MacosJamfProfilesTab.tsx
  • src/workspaces/macos-jamf/MacosJamfSelfServiceTab.tsx
  • src/workspaces/macos-jamf/MacosJamfTabStrip.tsx
  • src/workspaces/macos-jamf/MacosJamfWorkspace.tsx
  • src/workspaces/macos-jamf/index.ts
  • src/workspaces/macos-jamf/jamf-store.test.ts
  • src/workspaces/macos-jamf/jamf-store.ts
  • src/workspaces/macos-jamf/types.ts
  • src/workspaces/registry.ts
📝 Walkthrough

Walkthrough

Adds a macOS JAMF workspace with environment detection, JAMF Pro and Connect log parsing, profile filtering, log scanning, collection profiles, known sources, Tauri commands, frontend tabs, state management, and parser/store tests. It also extracts macOS profile details into a reusable drilldown component.

Changes

macOS JAMF workspace

Layer / File(s) Summary
JAMF contracts and command wiring
src-tauri/src/jamf/*, src-tauri/src/commands/jamf.rs, src-tauri/src/lib.rs
Adds typed JAMF models, shared paths, environment and log command handlers, profile filtering, feature-gated modules, and Tauri IPC registration.
Environment and log parsing
src-tauri/src/jamf/detect.rs, src-tauri/src/jamf/policy_log.rs, src-tauri/src/jamf/self_service.rs, src-tauri/src/jamf/connect.rs, src-tauri/src/jamf/profiles.rs
Detects JAMF installations and parses policy, Self Service, and Connect logs, including structured event classification, timestamp handling, and profile matching.
Workspace and collection registration
crates/cmtraceopen-parser/src/collector/*, src-tauri/src/commands/app_config.rs, src-tauri/src/commands/known_sources.rs, src-tauri/src/menu.rs, src/types/log.ts, src/workspaces/registry.ts, src/workspaces/macos-jamf/*
Registers the macos-jamf workspace, adds JAMF collection logs and commands, defines macOS known sources, and wires workspace availability and menu metadata.
JAMF frontend state and views
src/workspaces/macos-jamf/*
Adds Zustand slice state and tab views for environment, policies, profiles, Self Service, Connect, and scanned logs.
Profile drilldown extraction
src/workspaces/macos-diag/MacosDiagProfilesTab.tsx, src/workspaces/macos-diag/ProfileDrilldown.tsx, src/workspaces/macos-diag/index.ts
Moves expanded profile metadata and payload rendering into the reusable ProfileDrilldown component.
JAMF parser and state validation
src-tauri/tests/jamf_*, src/workspaces/macos-jamf/jamf-store.test.ts, crates/cmtraceopen-parser/src/collector/profile.rs
Adds tests for parsers, environment detection, known sources, collection filtering, missing files, and store lifecycle transitions.

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

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant MacosJamfWorkspace
  participant TauriCommands
  participant JamfEnvironment
  participant JamfLogParser
  User->>MacosJamfWorkspace: Open macOS JAMF workspace
  MacosJamfWorkspace->>TauriCommands: jamf_collect_environment
  TauriCommands->>JamfEnvironment: collect_environment_impl
  JamfEnvironment-->>MacosJamfWorkspace: JamfEnvironment
  User->>MacosJamfWorkspace: Select policy or Connect tab
  MacosJamfWorkspace->>TauriCommands: invoke log parser
  TauriCommands->>JamfLogParser: parse selected JAMF log
  JamfLogParser-->>MacosJamfWorkspace: typed events
  MacosJamfWorkspace-->>User: Render parsed JAMF data
Loading

Possibly related issues

  • Issue 314 — Implements the tracked macOS JAMF workspace, including backend parsers, known sources, profile handling, and frontend workspace integration.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.29% 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
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the main change: adding a macOS JAMF workspace.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks

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

@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: 12

🧹 Nitpick comments (2)
src/workspaces/macos-diag/ProfileDrilldown.tsx (2)

165-304: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No test coverage for the newly-extracted reusable component.

ProfileDrilldown centralizes metadata-grid and payload/settings-table rendering logic (including the parsePayloadData branching) that will apparently be reused by another workspace (per PR objectives). None of the provided files add component tests for it. Consider adding a basic render test (metadata fields present/absent, boolean/array settings rendering, raw-vs-parsed payload fallback).

🤖 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 `@src/workspaces/macos-diag/ProfileDrilldown.tsx` around lines 165 - 304, Add
component tests for ProfileDrilldown covering conditional metadata rendering,
boolean and array setting values, and the parsePayloadData branches that render
raw payload text when entries are empty versus the settings table when entries
exist. Reuse existing test utilities and provide representative profile and
payload fixtures.

245-297: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the payload-settings block into a helper for readability.

The payload data parsing/table rendering is an inline IIFE nested inside .map(), adding significant nesting depth to an already dense component. Consider extracting a small renderPayloadSettings(payload, metrics, styles) helper (or a PayloadSettings subcomponent) to flatten this.

🤖 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 `@src/workspaces/macos-diag/ProfileDrilldown.tsx` around lines 245 - 297, The
inline payload parsing and table-rendering IIFE inside the payload map makes the
component unnecessarily nested. Extract this block into a focused
renderPayloadSettings helper or PayloadSettings subcomponent that accepts
payload, metrics, and styles, preserves the existing empty-data fallback and
parsed settings rendering, and replace the IIFE with that helper/component.
🤖 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 `@src-tauri/src/commands/known_sources.rs`:
- Around line 824-840: Update the KnownSourceGroupingMetadata for the
macos-jamf-user-logs entry to use a neutral “User Logs” group instead of the
“Self Service” group, including the corresponding group_id and group_label while
preserving the source’s existing family and ordering metadata.

In `@src-tauri/src/jamf/detect.rs`:
- Around line 65-66: Update the JAMF detection result construction around
mdm_profile_present and mdm_organization so it no longer reports every device as
unmanaged: wire these fields to the available MDM profile collector before
release, or represent their unavailable state as unknown rather than false/None.
Preserve accurate managed-device reporting once profile data is available.
- Around line 10-12: Update the subprocess execution paths in read_jamf_version
and the related Jamf detection functions containing the four Command::output
calls so every jamf and PlistBuddy invocation is bounded by
JAMF_VERSION_TIMEOUT. Reuse a shared timeout-aware helper where appropriate, and
remove the unused/dead-code allowance once the constant is actively applied.

In `@src-tauri/src/jamf/policy_log.rs`:
- Around line 30-53: Centralize text decoding across the JAMF readers by
updating parse_policy_log_impl in src-tauri/src/jamf/policy_log.rs:30-53, the
reader in src-tauri/src/jamf/self_service.rs:38-50, and the reader in
src-tauri/src/jamf/connect.rs:39-51 to reuse the shared BOM-aware Windows-1252
decoder and propagate read/decode errors instead of silently truncating. Update
all four affected parsing sites in src-tauri/src/jamf/detect.rs:96-99, 110-111,
124-125, and 136-137 likewise, replacing UTF-8-only or lossy decoding with that
shared decoder.
- Around line 30-53: Refactor policy_log.rs lines 30-53, self_service.rs lines
38-50, and connect.rs lines 39-51 to use a shared async ingestion helper that
reads files through Tokio, preserves line order and offsets, propagates read
errors instead of silently stopping, and batch-parses lines with Rayon. Update
parse_policy_log_impl and the corresponding self-service/connect parsing flows
to consume this helper while preserving their existing event parsing and result
behavior.

In `@src-tauri/tests/jamf_environment.rs`:
- Around line 7-9: Remove the jamf_version assertion from the jamf_installed
branch in the environment test, so the test no longer requires a version when
Jamf is installed. Preserve the environment detection and any
non-version-related assertions.

In `@src-tauri/tests/jamf_policy_log_parsing.rs`:
- Around line 3-4: The JAMF integration tests must be gated behind the
macos-diag feature and executed in CI with that feature enabled. Add a
crate-level macos-diag cfg gate to src-tauri/tests/jamf_policy_log_parsing.rs at
lines 3-4 and src-tauri/tests/jamf_self_service_log_parsing.rs at line 3, then
configure a CI job or test step to run both suites with --features macos-diag.

In `@src/workspaces/macos-jamf/MacosJamfConnectTab.tsx`:
- Around line 14-33: Update the MacosJamfConnectTab environment handling around
installed, reload, and the useEffect so markNotInstalled("connect") runs only
after environment detection has resolved. Track or read the environment status,
avoid reloading while detection is pending, and trigger reload when installed
becomes true while preserving the existing idle-status guard and event loading
behavior.

In `@src/workspaces/macos-jamf/MacosJamfLogsTab.tsx`:
- Around line 51-53: The MacosJamfLogsTab summary incorrectly labels
scannedDirectories as “directories scanned.” Update the Caption1 text to say
“directories with logs,” preserving the existing count and formatting.

In `@src/workspaces/macos-jamf/MacosJamfPoliciesTab.tsx`:
- Around line 80-84: Update the second Tab in the TabList within
MacosJamfPoliciesTab to display “Summary” instead of “Timeline”, while
preserving its existing value and TriggerSummary rendering behavior.

In `@src/workspaces/macos-jamf/MacosJamfProfilesTab.tsx`:
- Around line 53-61: Update the profile row rendered in MacosJamfProfilesTab by
replacing the clickable li with a semantic button that supports native keyboard
focus and activation. Preserve the existing setSelected(p) behavior and selected
styling, while adapting the layout styles to the button element as needed.

In `@src/workspaces/macos-jamf/MacosJamfWorkspace.tsx`:
- Around line 55-60: Update MacosJamfWorkspace’s environment state rendering so
a failed environment load is surfaced instead of passing null to
MacosJamfEnvironmentBanner as ongoing detection. Use the existing environment
error state from envSlice to render the error and provide a retry action, while
preserving the current loading banner and normal environment rendering for
loading and successful states.

---

Nitpick comments:
In `@src/workspaces/macos-diag/ProfileDrilldown.tsx`:
- Around line 165-304: Add component tests for ProfileDrilldown covering
conditional metadata rendering, boolean and array setting values, and the
parsePayloadData branches that render raw payload text when entries are empty
versus the settings table when entries exist. Reuse existing test utilities and
provide representative profile and payload fixtures.
- Around line 245-297: The inline payload parsing and table-rendering IIFE
inside the payload map makes the component unnecessarily nested. Extract this
block into a focused renderPayloadSettings helper or PayloadSettings
subcomponent that accepts payload, metrics, and styles, preserves the existing
empty-data fallback and parsed settings rendering, and replace the IIFE with
that helper/component.
🪄 Autofix (Beta)

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6db974d4-0172-4d0b-a573-8c365beddf4f

📥 Commits

Reviewing files that changed from the base of the PR and between cf4fb41 and b2b6286.

⛔ Files ignored due to path filters (4)
  • src-tauri/tests/fixtures/jamf_connect_basic.log is excluded by !**/*.log
  • src-tauri/tests/fixtures/jamf_policy_log_basic.log is excluded by !**/*.log
  • src-tauri/tests/fixtures/jamf_policy_log_errors.log is excluded by !**/*.log
  • src-tauri/tests/fixtures/jamf_self_service_basic.log is excluded by !**/*.log
📒 Files selected for processing (39)
  • crates/cmtraceopen-parser/src/collector/profile.rs
  • crates/cmtraceopen-parser/src/collector/profile_data.json
  • src-tauri/src/commands/app_config.rs
  • src-tauri/src/commands/jamf.rs
  • src-tauri/src/commands/known_sources.rs
  • src-tauri/src/commands/mod.rs
  • src-tauri/src/jamf/connect.rs
  • src-tauri/src/jamf/detect.rs
  • src-tauri/src/jamf/mod.rs
  • src-tauri/src/jamf/models.rs
  • src-tauri/src/jamf/paths.rs
  • src-tauri/src/jamf/policy_log.rs
  • src-tauri/src/jamf/profiles.rs
  • src-tauri/src/jamf/self_service.rs
  • src-tauri/src/lib.rs
  • src-tauri/src/menu.rs
  • src-tauri/tests/jamf_connect_log_parsing.rs
  • src-tauri/tests/jamf_environment.rs
  • src-tauri/tests/jamf_known_sources.rs
  • src-tauri/tests/jamf_policy_log_parsing.rs
  • src-tauri/tests/jamf_self_service_log_parsing.rs
  • src/types/log.ts
  • src/workspaces/macos-diag/MacosDiagProfilesTab.tsx
  • src/workspaces/macos-diag/ProfileDrilldown.tsx
  • src/workspaces/macos-diag/index.ts
  • src/workspaces/macos-jamf/MacosJamfConnectTab.tsx
  • src/workspaces/macos-jamf/MacosJamfEnvironmentBanner.tsx
  • src/workspaces/macos-jamf/MacosJamfLogsTab.tsx
  • src/workspaces/macos-jamf/MacosJamfOverviewTab.tsx
  • src/workspaces/macos-jamf/MacosJamfPoliciesTab.tsx
  • src/workspaces/macos-jamf/MacosJamfProfilesTab.tsx
  • src/workspaces/macos-jamf/MacosJamfSelfServiceTab.tsx
  • src/workspaces/macos-jamf/MacosJamfTabStrip.tsx
  • src/workspaces/macos-jamf/MacosJamfWorkspace.tsx
  • src/workspaces/macos-jamf/index.ts
  • src/workspaces/macos-jamf/jamf-store.test.ts
  • src/workspaces/macos-jamf/jamf-store.ts
  • src/workspaces/macos-jamf/types.ts
  • src/workspaces/registry.ts

Comment thread src-tauri/src/commands/known_sources.rs
Comment thread src-tauri/src/jamf/detect.rs Outdated
Comment thread src-tauri/src/jamf/detect.rs Outdated
Comment thread src-tauri/src/jamf/policy_log.rs
Comment thread src-tauri/tests/jamf_environment.rs Outdated
Comment thread src/workspaces/macos-jamf/MacosJamfConnectTab.tsx Outdated
Comment thread src/workspaces/macos-jamf/MacosJamfLogsTab.tsx
Comment thread src/workspaces/macos-jamf/MacosJamfPoliciesTab.tsx Outdated
Comment thread src/workspaces/macos-jamf/MacosJamfProfilesTab.tsx Outdated
Comment thread src/workspaces/macos-jamf/MacosJamfWorkspace.tsx Outdated
@adamgell

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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

@adamgell adamgell added feature New feature jamf JAMF/macOS MDM related workspace Workspace UI area enhancement New feature or request labels Jul 31, 2026
@adamgell adamgell added this to the macOS JAMF Workspace milestone Jul 31, 2026
adamgell and others added 14 commits July 31, 2026 13:57
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add stub modules (policy_log, self_service, connect, profiles) and the
commands/jamf.rs IPC layer, then register the JAMF module and its six
commands in lib.rs/commands/mod.rs (gated on the macos-diag feature
since the JAMF module reuses MacosMdmProfile/MacosLogFileEntry/FdaStatus).

Drop unused Deserialize derives from JamfEnvironment, JamfLogScanResult,
and JamfProfilesResult — these are command-return-only and embed
Serialize-only types from macos_diag::models, so the derives could not
compile.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds 7 macOS known log sources covering the JAMF binary, JAMF helper
logs, package receipts, Self Service, and JAMF Connect (system + per-
user). Promotes the `commands` module to `pub` so the new integration
test can reach `commands::known_sources` via `app_lib`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds 5 log entries (jamf.log, app-support, per-user, JAMFConnect daemon,
JAMFConnect per-user) and 2 commands (jamf version, jamf checkJSSConnection)
to the embedded collection profile, plus a regression test asserting the
family filter retains only macos-jamf items.

Note: the new test cannot currently run via CI because of a pre-existing
workspace-root conflict between root Cargo.toml and src-tauri/Cargo.toml's
detached [workspace]. JSON validity verified by python json parser; will
become live once the workspace structure is reconciled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captured from a JAMF Pro 11 host: recurring check-ins, manual policy
runs, policy-id triggered checks, an Executing Policy line, and a
recon timeout error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Verifies collect_environment_impl never panics regardless of host JAMF
state, and that the jamf_log directory flag agrees with the actual
filesystem. Asserts the version is populated whenever the binary is
detected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the Rust types from src-tauri/src/jamf/models.rs into TS, and
adds "macos-jamf" to the WorkspaceId union.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
adamgell and others added 24 commits July 31, 2026 13:57
fs::File::open does not expand ~, so the frontend's "~/Library/..." path
would have failed to resolve. Make the path argument optional and have
the backend default to paths::self_service_log_file(), which produces a
real expanded PathBuf. The frontend now passes null and shows the
"~/..." form purely as a display label.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the WorkspaceDefinition export and inserts it into ALL_WORKSPACES
immediately after macosDiagWorkspace. The workspace gates on the macos
platform per the design (always visible on macOS, even when JAMF isn't
detected, so analysts can open bundles from JAMF-managed Macs).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The frontend workspace registry filters against get_available_workspaces()
returned by the backend. macos-jamf was registered frontend-side in the
WorkspaceDefinition map but never added to the backend list, so the
filter dropped it before it could reach the toolbar dropdown.

Add it under the macos-diag feature gate (matching how the jamf module
itself is gated in lib.rs and commands/mod.rs).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The branch predates main's every_backend_available_workspace_has_a_menu_
descriptor invariant, so exposing macos-jamf in the backend allowlist left
it without a WORKSPACE_DESCRIPTORS entry and the test failed. Registers it
under Endpoint Management, alongside Intune as the other MDM, gated to
macOS to match the workspace's own platforms list.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI runs `cargo test --no-default-features` and
`cargo clippy --no-default-features --all-targets -- -D warnings` on
ubuntu-latest. Those turn off `macos-diag`, but integration test targets
are still compiled, so four suites failed with
`cannot find 'jamf' in 'app_lib'` and took the whole Rust job down.

Add the inner attribute the repo already uses for feature-scoped suites
(see tests/dns_audit_real.rs). jamf_known_sources.rs needed no change: it
is target-gated and carries a negative non-macOS assertion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
JamfPolicyTrigger and JamfPolicyResult carry data, and serde's default
external tagging serialized them as a bare string for unit variants and
`{"policyId":"332"}` for the rest. The frontend declares internally
tagged unions and switches on `.type`
(src/workspaces/macos-jamf/types.ts), so every Trigger and Result cell
rendered blank and the summary view collapsed all events into a single
`undefined` bucket.

Switch both enums to adjacent tagging, which produces exactly the shape
TypeScript already expects: `{"type":"recurringCheckIn"}` and
`{"type":"policyId","value":"332"}`.

Nothing in the suite could catch this: tsc does not see the Rust side,
the parser tests use the enums directly without serializing, and the
store test only ever builds an empty `events` array. Add
jamf_ipc_contract.rs to pin the wire format on both sides.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
parse_system_profiler_plist walked to `section_items.first()` and stopped.
`system_profiler SPConfigurationProfileDataType` emits one section per
scope: a "User (<uid>) Configuration Profiles" section and a
device-level `spconfigprofile_section_deviceconfigprofiles` section. Only
the user scope was ever read.

On a Jamf-managed reference host that is 1 of 32 profiles — every
device-scoped profile, which is the large majority on any managed Mac,
was silently invisible. This is pre-existing and affects the macOS
Diagnostics Profiles tab as much as the new JAMF one.

Flatten across all sections instead. Verified on the reference host:
enumerated profiles go from 1 to 32.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
matches_jamf tested `payload_type` against `com.jamfsoftware.` /
`com.jamf.`, but that field is populated from the plist's `_name`, which
system_profiler fills with the *Apple* payload type — a JAMF-deployed
PPPC payload reports `com.apple.TCC.configuration-profile-policy`. The
JAMF identity lives in `payload_identifier`, e.g.
`com.jamfsoftware.tcc.management`.

So the prefix test matched nothing at all: on the reference host the
Profiles tab listed 0 of 32 profiles. Check both fields.

Note the organization fallback cannot compensate today, because
`expected_organization` is threaded from `mdm_organization`, which
detect.rs still hardcodes to None.

Verified on the reference host: 5 JAMF profiles now match, spanning
orgs "Jamf Software", "JAMF Software", "Jamf" and "CDW LLC".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three defects in environment collection, all found against a live
Jamf-managed host:

* The IdP was read from `/Library/Preferences/com.jamf.connect.plist`,
  which does not exist on a managed Mac — JAMF Connect settings arrive by
  MDM and land in `/Library/Managed Preferences`. The key also differs by
  domain: `OIDCProvider` in `com.jamf.connect.login.plist`, `Provider` in
  `com.jamf.connect.plist`. Replace the single path with an ordered
  (plist, key) candidate list, keeping the original pair last as a legacy
  fallback. The reference host now resolves the IdP as "Azure" instead of
  reporting none.

* Installation was detected solely from `/Applications/JAMF Connect.app`.
  Jamf dropped the all-caps branding years ago, and the menu-bar app is
  not always deployed even where the package is. Accept either bundle
  casing and treat `/Library/Application Support/JamfConnect` as
  evidence too; the reference host reports installed instead of
  "not detected".

* read_jamf_version called Command::output() with no deadline while
  JAMF_VERSION_TIMEOUT sat unused behind #[allow(dead_code)]. The jamf
  binary talks to the JSS, so a degraded network could stall a Tauri
  command indefinitely. Run it on a helper thread with a recv_timeout so
  a wedged process cannot hold the IPC call open, and drop the dead-code
  allowance.

Also fixes an early return in the IdP loop that abandoned the remaining
candidates when PlistBuddy failed to spawn.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The three JAMF parsers each mishandled real-world input in a different
way, and none followed the UTF-8 -> Windows-1252 fallback the main parser
uses (cmtraceopen_parser::parser::decode_bytes).

Malformed input. policy_log used BufRead::read_line, which returns
ErrorKind::InvalidData on a single non-UTF-8 byte and failed the entire
file. self_service and connect used lines().map_while(Result::ok), which
silently stops at the first bad line — worse than failing, because events
after the fault vanish with no error and no indication. Add jamf::text,
which reads with read_until and decodes per line with the Windows-1252
fallback, so bad bytes degrade to replacement characters instead of
losing data. Genuine I/O errors still propagate.

Timestamps. jamf.log and selfservice.log record the host's local wall
clock with no offset, but both parsers wrapped the naive value in
Utc.from_utc_datetime, labelling local time as UTC and skewing every
event by the local offset. That fed straight into last_check_in and made
JAMF events unalignable with sources that do carry an offset. Add
jamf::time, resolving through the host zone with explicit policies for
the DST-ambiguous and nonexistent cases. Verified on the reference host:
`Thu Jul 30 01:17:39` at -04:00 now yields 2026-07-30T05:17:39Z, not
01:17:39Z.

Zulu offsets. connect::parse_timestamp documented support for
`...T09:12:03Z`, but chrono's %z rejects the Z designator, so those lines
failed to parse and were dropped. Normalize Z to +0000 and use %#z so
`+0000` and `+00:00` are both accepted. The has_tz heuristic was also
unreachable, since the line regex already requires a zone marker.

Reading bytes additionally removes a stream_position() call per line —
one lseek syscall per line — since offsets are now tracked arithmetically.

Adds jamf_parser_robustness.rs covering non-UTF-8 input, truncated final
lines, offset accuracy, and the four zone spellings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The environment slice can fail, but nothing in the workspace rendered
that:

* The banner and the Overview tab both treated "no data" as "still
  scanning", so a failed jamf_collect_environment showed "Detecting
  JAMF..." forever with no way to retry. Give the banner an error state
  with a Retry action, hoist the environment load into a callback so it
  can be re-invoked, and give Overview its own error branch.

* The JAMF Connect tab's notInstalled state was terminal. `installed` is
  false until the environment resolves, so an early first pass could mark
  the tab notInstalled; the `status === "idle"` guard then blocked any
  retry, the store outlives unmounts, and that branch rendered no reload
  control. Re-run when `installed` becomes true, and add a Re-check
  button. Its message no longer names a specific bundle path, since
  detection now accepts several.

* The Profiles tab fetched on mount, before the environment resolved, so
  expectedOrganization was pinned to null for the life of the slice. Wait
  for the environment to settle first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jamf_parse_self_service_log filtered out an empty string before building
a PathBuf, but jamf_parse_policy_log and jamf_parse_connect_log did not,
so Some("") became PathBuf::from("") and tried to open the current
directory instead of falling back to the canonical log path.

Extract the shared resolve_path helper: trim, treat empty as None, expand
a leading `~/`, otherwise use the value as given. All three commands now
behave the same way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jamf_sources() and the assertions that use it are #[cfg(target_os =
"macos")], so on every other target KnownSourceMetadata is imported and
never used. Under `-D warnings` that is an error, and it took down two CI
jobs on the previous run for the same reason:

  Check & Test (Rust)        Rust clippy (lite), ubuntu-latest
  ESP Diagnostics (Windows)  cargo clippy --all-features, windows-latest

Both were already failing on b2b6286, before the preceding commits on
this branch.

The lint is invisible on macOS, where the import is genuinely used —
verified by inverting the cfg predicates locally to compile the
non-macOS arm, which reproduces the error before this change and is
clean after it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
for_each_line delegated to BufRead::read_until, which grows its buffer to
the next delimiter. A file with no newline at all — a truncated binary
blob, or the wrong file matched by a `*.log` glob — was therefore pulled
into memory whole as one "line", and then retained again inside every
event's raw_line.

Read through fill_buf/consume with a 64 KiB cap instead: keep at most
that much of a record, mark the remainder with an explicit truncation
marker so a clipped line reads as clipped, and drain the rest so offset
arithmetic still reflects true file positions.

Raised by CodeRabbit against the parser-hardening commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two follow-ups from the CodeRabbit pass, both cases of papering over a
missing resource rather than reporting it:

* output_with_timeout ran the command on a detached thread and abandoned
  it on timeout, so a wedged `jamf` process survived every collection.
  Own the child instead: spawn it, poll try_wait against a deadline, and
  kill and reap it if the deadline passes. Draining the pipes only after
  exit is safe here because these commands emit far less than a pipe
  buffer.

* home_dir fell back to /tmp when HOME was unset, silently pointing every
  per-user path at a world-writable shared directory. Return Option
  instead and let it propagate: scan_directories reports the per-user
  sources as absent, jamf_scan_logs drops them from the candidate list,
  and the parse commands surface a NotFound rather than reading somewhere
  unrelated. An explicit `~/` argument still expands when HOME is
  available and is left literal when it is not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
collect_environment_impl returned three hardcoded placeholders — a
`// Filled in Task 1.5 wiring` marker on mdm_profile_present, None for
mdm_organization, and FdaStatus::Unknown — and the Overview tab rendered
all three as if they were probe results. For a diagnostics tool that is
worse than showing nothing.

Derive them instead:

* Extract the TCC.db probe out of macos_diag::environment::
  scan_environment_impl into a shared detect_full_disk_access(), with an
  Unknown arm off macOS, so both workspaces answer from one signal.

* Read the MDM enrollment profile from list_profiles_impl: presence is
  the com.apple.mdm payload or an enrolled `profiles status`, and the
  organization is the one stamped on that profile. Failure is swallowed —
  enumeration is unavailable off macOS and can be denied at runtime, and
  neither should fail the whole environment probe.

Wiring the organization has a visible effect: it is what
filter_jamf_profiles_impl matches on, so the Profiles tab goes from
recognizing only profiles with JAMF-specific payloads to recognizing
everything that MDM deployed — 5 to 32 on the reference host, which is
the true count there.

That match is only meaningful when the MDM is JAMF, so the Profiles tab
passes the organization only when the jamf binary is present; on a Mac
managed by another vendor it falls back to payload matching rather than
relabelling that vendor's profiles.

Also format last_check_in through the reader's locale instead of printing
the raw UTC ISO string, which read as local time while being UTC.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
duration_ms was the last always-null field on the JAMF wire type: the
parser set it to None unconditionally, types.ts declared it, and no view
read it — a promise the payload never kept.

jamf.log has no policy-completion marker, but every line carries the
emitting jamf PID, which the regex already captured and then discarded.
One invocation is one PID, and a check-in may execute several policies in
sequence, so a policy's elapsed time is the span from its
`Executing Policy` line to the next policy started by the same PID, or to
that invocation's last line.

Two things this is careful about:

* Concurrent invocations interleave in syslog, so a foreign PID between
  two lines does not end the run.
* PIDs are recycled. Pairing across a reuse would report a policy that
  ran for days, so a span is only attributed within a 12-hour window —
  comfortably above the hour-plus inventory runs that genuinely occur.

The figure is the invocation's own elapsed time, not something JAMF
reports: trailing work after the final policy lands in that policy's
total, making it an upper bound. That is the useful direction for "what
is making check-ins slow", and it is documented as such rather than
presented as a vendor metric.

Surfaced as an Elapsed column, alongside rendering both timestamp columns
in the reader's locale instead of as raw UTC ISO strings.

On the reference log all 33 policy executions resolve, the slowest being
a 65-minute inventory run that the raw log confirms.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
detect_full_disk_access has both a macOS and a non-macOS arm, but
FdaStatus was imported under #[cfg(target_os = "macos")] along with the
types only the macOS implementation uses. The non-macOS arm therefore
failed to resolve its own return type, breaking the Rust MSRV job on
ubuntu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Opening the JAMF workspace threw at runtime:

  ReferenceError: Cannot access 'macosDiagWorkspace' before initialization

MacosJamfProfilesTab imported ProfileDrilldown from the ../macos-diag
barrel, but that module also constructs macosDiagWorkspace. Reaching
ProfileDrilldown through it makes this workspace re-enter
macos-diag/index.ts while that module is still initializing, and the
workspace binding is in its temporal dead zone.

Import from the module directly. The barrel still re-exports
ProfileDrilldown for anyone who wants it; it is now reachable only from
registry.ts, so there is no cycle.

Neither tsc nor the test suite can see this — it only appears when the
app actually loads the workspace, which is how it was found.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every tab rendered its full contents with no scroll container. The
Policies table alone is ~2700 rows on a six-week jamf.log, so the views
ran off the bottom with no way to reach the end.

Give the shell the same shape the macOS Diagnostics workspace uses: the
root is a fixed-height flex column with overflow hidden, and the tab body
is flex:1 with overflow auto and minHeight:0. Fixing it here rather than
per tab means all six scroll, and the environment banner and tab strip
stay put while content moves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A policy that installs software writes the outcome as two lines the
parser lumped into the generic other/info bucket:

  Installing Zscaler-osx-4.5.2.312-installer.pkg...
  Successfully installed Zscaler-osx-4.5.2.312-installer.pkg.

Those are the only record that a policy delivered anything —
"Executing Policy X" says it started, not that it worked — and being
indistinguishable from routine chatter meant any view that suppressed
noise suppressed them too.

Classify them as an `install` trigger carrying the package name, with
InProgress for the start, Success for the completion (trailing period
trimmed), and Failure for "Failed to install".

Regression test built from a real enforcement sequence observed on a
managed host: custom trigger fires a policy, the policy installs a
package, the package reports success.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The tab dumped every parsed line into one flat table. On a six-week
jamf.log that is ~2700 rows, of which 33 are policy runs and the rest is
bookkeeping — launchd task cleanup, "No patch policies were found" — so
the events that describe what the Mac actually did were unfindable. It
reported data without telling you anything.

Restructured around what someone opening this tab wants to know:

* A stat strip: date span, check-ins, policy runs, installs, failures
  (red when non-zero), last check-in, and the slowest run with its name.
* Filters, each showing its own count — Activity (default; suppresses
  the routine chatter but never a failure), Policies, Installs,
  Failures, All. Nothing is hidden: All still lists every row.
* A By-day view replacing the bare per-trigger counts, one line per day:
  check-in count, what ran and how long it took, what was installed, and
  failures called out. That surfaces patterns a flat table cannot — a
  package reinstalling every few days reads as a self-healing policy
  fighting something, which is invisible row by row.
* Sticky table header, local-time timestamps, and the Policy column
  renamed "Policy / package" now that install events populate it.

isPolicyRun keys on the `execute` trigger rather than "has a policy
name", so install events do not inflate the policy-run count.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Move the catch-all JAMF user-logs known source out of the Self Service
  group into a neutral User Logs group; the folder also holds JAMF
  Connect and debug logs
- Bound the three PlistBuddy probes with the same timeout wrapper the
  jamf binary calls use, so a wedged process cannot stall the
  environment probe
- Drop the jamf-installed-implies-version test assertion; an existing
  binary does not guarantee `jamf version` succeeds. Assert the inverse
  invariant instead
- Reword the Logs tab summary to 'directories with logs'; the scan only
  returns directories that contained files
- Make profile rows keyboard-accessible buttons with aria-pressed

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Exercised against logs captured from a live JAMF-managed Mac (jamf.log
2730 lines, selfservice.log, selfservice_debug.log, 32-profile
system_profiler capture). Findings:

- The Self Service parser expected '<ts> [LEVEL] Browse|Install|Cancel:'
  lines. That format does not exist; a real selfservice.log parsed to
  zero events. The real format is '[YYYY-MM-DD HH:MM:SS] message' with
  Request:, Binary Request: (triggerPolicy, doRecon), WARNING:, and
  connectivity-state vocabulary. Parser, synthetic fixture, and tests
  rewritten to match reality
- jamf.log parses 100 percent of real lines; classifier vocabulary
  confirmed against the capture
- Classify 'Could not connect to the JSS' check-ins as connectivity
  failures instead of generic info; a run of these is what an
  off-network Mac looks like
- New env-gated integration suite (tests/jamf_real_fixtures.rs) that
  runs the parsers over the private capture when JAMF_DEV_FIXTURES is
  set; skips vacuously in CI. parse_system_profiler_plist made pub so
  the captured profiles XML exercises the live code path
- Redact a real hostname and account name that had leaked into the
  committed synthetic fixtures

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@adamgell
adamgell force-pushed the feat/jamf-workspace branch from cb001ff to d2ab9c8 Compare July 31, 2026 18:10
@adamgell

Copy link
Copy Markdown
Owner Author

Pushed two commits after rebasing onto current main (one conflict, same both-kept test collision in collector/profile.rs as last time):

173e9c8 addresses the five still-valid review findings: neutral User Logs group for the catch-all source, bounded PlistBuddy probes, corrected environment-test invariant, 'directories with logs' wording, and keyboard-accessible profile rows. The other four unresolved threads were already fixed on the branch (effect timing, Zulu timestamps, centralized CP1252 decoding, Timeline rename); all nine are now resolved with pointers.

d2ab9c8 is the first run of these parsers against data from a real JAMF-managed Mac, which the PR description flagged as unverified:

  • Real defect found and fixed: the Self Service parser expected <ts> [LEVEL] Browse|Install|Cancel: lines. That format does not exist. A real selfservice.log parsed to zero events. The parser now handles the actual format ([ts] Request:/Binary Request:/WARNING: plus connectivity states), and the committed synthetic fixture was rewritten to match reality.
  • jamf.log: all 2,730 real lines parse; classifier vocabulary confirmed. Added classification for 'Could not connect to the JSS' check-ins as connectivity failures.
  • Profiles: the 32-profile system_profiler capture parses and filters to JAMF profiles through the live code path.
  • New env-gated suite tests/jamf_real_fixtures.rs runs all of this when JAMF_DEV_FIXTURES points at the private capture; CI skips it vacuously.
  • Redacted a real hostname and account name that had leaked into the committed fixtures.

All gates green locally: cargo test (including the real-fixture suite), clippy -D warnings, tsc, 529/529 frontend tests.

🤖 Generated with Claude Code

@adamgell
adamgell marked this pull request as ready for review July 31, 2026 18:27
@adamgell
adamgell merged commit 0e1baa9 into main Jul 31, 2026
16 checks passed
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 jamf JAMF/macOS MDM related workspace Workspace UI area

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants