Skip to content

feat: AppWorkload enrichment, activity view, GUID registry - #81

Merged
adamgell merged 4 commits into
mainfrom
feature/multi-feature-batch
Apr 2, 2026
Merged

feat: AppWorkload enrichment, activity view, GUID registry#81
adamgell merged 4 commits into
mainfrom
feature/multi-feature-batch

Conversation

@adamgell

@adamgell adamgell commented Apr 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • Log viewer enrichment: Parse "Get policies" JSON payloads to build GUID→Name mappings, show resolved app names in InfoPane, display structured policy metadata and decoded PowerShell scripts via ScriptCodeViewer
  • AppWorkload parser fixes: Fix rotated file detection (prefix match), fix GUID extraction preferring app GUIDs over user GUIDs, add SideCarScriptDetectionManager event extraction
  • Activity view: New toggle in Intune timeline groups events by app into collapsible cards with parsed structured fields (intent, detection, applicability, reboot, GRS) as colored tags, inline GUID resolution, and word-wrapped detail messages
  • GUID Registry dialog: New Tools menu item showing searchable table of all GUID→Name mappings with source confidence, click-to-copy
  • Resizable InfoPane: Drag handle between log list and detail pane

Test plan

  • Open AppWorkload logs in log viewer — verify "Get policies" entries show structured policy cards with decoded scripts
  • Select log entries containing GUIDs — verify resolved app names appear in InfoPane
  • Open rotated AppWorkload files (e.g., AppWorkload-20260401-160729.log) — verify they parse as LogicalRecord, not PhysicalLine
  • Run Intune analysis, switch to Activity view — verify events group by app with colored status tags
  • Expand activity groups — verify detail messages show resolved app names instead of GUIDs
  • Tools → GUID Registry — verify searchable table of GUID→Name mappings
  • cargo test from src-tauri/ — all tests pass
  • npx tsc --noEmit — no type errors

🤖 Generated with Claude Code

adamgell and others added 2 commits April 1, 2026 20:46
…ARIA navigation

- Add .catch(console.error) to all writeText() calls in use-context-menu.ts
- Return per-record parse_errors from parse_single_file and aggregate in parse_evtx_files
- Use "Unknown" fallback for missing channel, provider, and computer fields
- Fix misleading live.rs stub error message to "not yet implemented"
- Add ArrowLeft/ArrowRight/Home/End keyboard navigation to SettingsDialog tablist

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Parse "Get policies" JSON in log viewer to extract GUID→Name mappings
- Show resolved app names in InfoPane when log messages contain GUIDs
- Add SideCarScriptDetectionManager event extraction (PowerShellScript type)
- Fix rotated AppWorkload files not parsed (prefix match in detect.rs)
- Fix GUID extraction preferring "for app" over user GUIDs
- Add policy_parser.rs for extracting app policy metadata from IME logs
- Add ScriptCodeViewer with lightweight PowerShell syntax highlighting
- Add AppWorkloadScriptDetail for structured "Get policies" display
- Make InfoPane resizable via drag handle
- Add Activity view toggle in Intune timeline (grouped by app)
- Parse structured fields (intent, detection, applicability) into colored tags
- Resolve GUIDs inline in activity view detail messages
- Add GUID Registry dialog under Tools menu
- Store guidRegistry from Intune analysis results in frontend store

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 2, 2026 12:05
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

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 enhances the Intune/AppWorkload diagnostics experience by extracting and surfacing richer app identity and policy/script metadata, adding an “Activity” grouped timeline view, and exposing GUID mappings via a dedicated registry dialog.

Changes:

  • Add GUID→Name enrichment from AppWorkload “Get policies” payloads and display resolved names/scripts in the log InfoPane.
  • Introduce an Intune timeline view toggle that switches from a flat list to an app-grouped “Activity” view with parsed tags and GUID resolution.
  • Add a Tools → GUID Registry dialog plus backend policy metadata extraction and script-body attachment to PowerShell script events.

Reviewed changes

Copilot reviewed 31 out of 33 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
src/types/intune.ts Extends shared Intune types with policy metadata and script fields.
src/stores/ui-store.ts Adds UI state for GUID Registry dialog visibility.
src/stores/log-store.ts Stores GUID→Name map derived from loaded log entries.
src/stores/intune-store.ts Adds timeline view mode and stores policyMetadata/guidRegistry.
src/lib/guid-name-map.ts New utility to parse “Get policies” and resolve GUIDs in messages.
src/hooks/use-context-menu.ts Ensures clipboard writes are awaited/handled.
src/hooks/use-app-menu.ts Handles new menu action to open GUID Registry dialog.
src/components/log-view/InfoPane.tsx Displays resolved GUID names and adds script detail rendering.
src/components/log-view/AppWorkloadScriptDetail.tsx New component to parse/display policy cards and sidecar script details.
src/components/layout/Toolbar.tsx Passes guidRegistry/policyMetadata into Intune result metadata flow.
src/components/layout/AppShell.tsx Adds resizable InfoPane handle and mounts GUID Registry dialog.
src/components/intune/ScriptCodeViewer.tsx New script viewer with tokenization + copy-to-clipboard.
src/components/intune/IntuneDashboardNavBar.tsx Adds List/Activity view toggle; hides sort controls in Activity mode.
src/components/intune/EventTimelineRow.tsx Shows decoded script bodies inline for script events.
src/components/intune/EventTimeline.tsx Switches between list timeline and activity-grouped view.
src/components/intune/EventActivityView.tsx New virtualized, collapsible app-grouped activity timeline view.
src/components/dialogs/SettingsDialog.tsx Adds keyboard navigation support to settings tablist.
src/components/dialogs/GuidRegistryDialog.tsx New searchable GUID registry dialog with click-to-copy rows.
src-tauri/src/parser/detect.rs Improves rotated IME/AppWorkload file detection via prefix match.
src-tauri/src/menu.rs Adds Tools → GUID Registry menu item and action payload mapping.
src-tauri/src/intune/timeline.rs Updates tests/fixtures for new IntuneEvent fields.
src-tauri/src/intune/policy_parser.rs New backend parser for “Get policies” payloads + script decode.
src-tauri/src/intune/models.rs Adds policy metadata models and script fields to IntuneEvent/result.
src-tauri/src/intune/mod.rs Exposes the new policy_parser module (diagnostics feature gated).
src-tauri/src/intune/event_tracker.rs Improves GUID extraction and adds sidecar script detection events.
src-tauri/src/event_log/parser.rs Tracks per-record EVTX parse errors; improves “Unknown” defaults.
src-tauri/src/event_log/live.rs Updates unimplemented live event log error messaging.
src-tauri/src/commands/intune.rs Aggregates policy metadata and attaches decoded scripts to script events.
src-tauri/Cargo.toml Adds base64 dependency for script decoding.
src-tauri/Cargo.lock Locks base64 dependency version.
package-lock.json Bumps app version in lockfile.
docs/superpowers/specs/2026-04-01-intune-gantt-view-design.md Adds design spec for the (named) Gantt/Activity timeline concept.

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

Comment thread src/lib/guid-name-map.ts Outdated
const additions = buildGuidNameMap(newEntries);
if (Object.keys(additions).length === 0) return existing;

return { ...existing, ...additions };

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

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

mergeGuidNameMap claims it “does not overwrite existing names”, but the spread order return { ...existing, ...additions } will overwrite any existing GUID with a newly parsed name. Either reverse the spread order or explicitly only add keys that don’t already exist (and update the doc if overwriting is intended).

Suggested change
return { ...existing, ...additions };
return { ...additions, ...existing };

Copilot uses AI. Check for mistakes.
window.addEventListener("mouseup", onMouseUp);
return () => {
window.removeEventListener("mousemove", onMouseMove);
window.removeEventListener("mouseup", onMouseUp);

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

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

The resize useEffect cleans up event listeners on unmount, but if the component unmounts while a drag is in progress, document.body.style.cursor / userSelect will remain set. Consider resetting those styles in the cleanup function when infoPaneResizeRef.current is non-null.

Suggested change
window.removeEventListener("mouseup", onMouseUp);
window.removeEventListener("mouseup", onMouseUp);
if (infoPaneResizeRef.current) {
infoPaneResizeRef.current = null;
document.body.style.cursor = "";
document.body.style.userSelect = "";
}

Copilot uses AI. Check for mistakes.
Comment on lines +153 to +177
return (
<tr
style={{ cursor: "pointer" }}
onClick={handleCopyGuid}
title="Click to copy GUID"
>
<td style={{ ...tdStyle, fontWeight: 500, color: tokens.colorNeutralForeground1 }}>
{entry.name}
</td>
<td style={{ ...tdStyle, color: tokens.colorNeutralForeground3, fontSize: "11px" }}>
{guid}
</td>
<td style={tdStyle}>
<span
style={{
fontSize: "10px",
fontWeight: 600,
color: sourceInfo.color,
}}
>
{sourceInfo.label}
</span>
</td>
</tr>
);

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

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

Rows are clickable to copy, but <tr> isn’t keyboard-focusable and doesn’t expose an accessible button role/action, so keyboard/screen-reader users can’t trigger the copy behavior. Consider rendering a <button> inside the cell (or adding tabIndex, role="button", and onKeyDown handlers) and providing an accessible label like “Copy GUID …”.

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +40
function decodeBase64(encoded: string): string | null {
try {
return atob(encoded);
} catch {
return null;
}
}

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

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

decodeBase64 uses atob, which returns a binary/Latin-1 string and will corrupt non-ASCII PowerShell scripts. Prefer decoding to bytes and converting with TextDecoder (and/or handling UTF-16LE if these scripts can be encoded that way) so scripts display correctly.

Copilot uses AI. Check for mistakes.
Comment on lines +81 to +87
return arr.map((item: unknown) => {
const obj = item as Record<string, unknown>;
const entry: PolicyEntry = {
id: String(obj.Id ?? ""),
name: String(obj.Name ?? "Unknown"),
intent: typeof obj.Intent === "number" ? obj.Intent : undefined,
targetType: typeof obj.TargetType === "number" ? obj.TargetType : undefined,

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

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

parseGetPolicies coerces missing Id to an empty string (id: String(obj.Id ?? "")). If multiple items lack Id, React keys (key={policy.id}) will collide and the UI may render incorrectly. Consider skipping entries without a valid non-empty Id (and ideally validating it looks like a GUID).

Copilot uses AI. Check for mistakes.
Comment on lines +204 to +223
// Attach decoded script bodies to PowerShellScript events from policy metadata
if !all_policy_metadata.is_empty() {
for event in &mut all_events {
if event.event_type == IntuneEventType::PowerShellScript {
let lookup_guid = event
.parent_app_guid
.as_deref()
.or(event.guid.as_deref());
if let Some(guid) = lookup_guid {
if let Some(policy) = all_policy_metadata.get(guid) {
// Find the first script-type detection rule with a body
if let Some(rule) = policy
.detection_rules
.iter()
.find(|r| r.detection_type == 3 && r.script_body.is_some())
{
event.script_body = rule.script_body.clone();
}
}
}

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

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

Policy metadata is keyed by policy.id as-is, but later lookups use event.parent_app_guid / event.guid as-is. If casing differs between the policy payload and sidecar/script events, all_policy_metadata.get(guid) will miss and script_body won’t attach. Consider normalizing GUID keys consistently (e.g., store policy IDs lowercased and lowercase the lookup GUID before get).

Copilot uses AI. Check for mistakes.
if (name.length === 0) continue;
// If it's a GUID, try to resolve it via the registry
if (GUID_PATTERN.test(name)) {
const entry = registry?.[name.toLowerCase()];

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

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

extractAppNameFromDetail assumes guidRegistry is keyed by lowercase GUIDs (registry?.[name.toLowerCase()]). If the backend sends GUIDs with original casing, this lookup will miss and grouping/labels will fall back to raw GUIDs. Consider either normalizing the registry keys up-front (preferred) or checking both registry[name.toLowerCase()] and registry[name] here (similar to resolveGuidsInText).

Suggested change
const entry = registry?.[name.toLowerCase()];
const lowerKey = name.toLowerCase();
const entry = registry?.[lowerKey] ?? registry?.[name];

Copilot uses AI. Check for mistakes.
Comment thread src/lib/guid-name-map.ts Outdated
const additions = buildGuidNameMap(newEntries);
if (Object.keys(additions).length === 0) return existing;

return { ...existing, ...additions };

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

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

This new parsing/merging logic is non-trivial (JSON sanitization, extraction, merge semantics) but there are no accompanying unit tests. Since src/lib already has Vitest coverage (e.g., src/lib/column-config.test.ts), consider adding tests for buildGuidNameMap/mergeGuidNameMap/resolveGuidsInMessage, including invalid-escape JSON and merge behavior.

Suggested change
return { ...existing, ...additions };
// Existing entries should not be overwritten; let them take precedence.
return { ...additions, ...existing };

Copilot uses AI. Check for mistakes.
Comment thread src/stores/ui-store.ts
Comment on lines 423 to 430
setShowFindBar: (show) => set({ showFindBar: show }),
setShowFilterDialog: (show) => set({ showFilterDialog: show }),
setShowErrorLookupDialog: (show) => set({ showErrorLookupDialog: show }),
setShowAboutDialog: (show) => set({ showAboutDialog: show }),
setShowSettingsDialog: (show) => set({ showSettingsDialog: show }),
setShowEvidenceBundleDialog: (show) => set({ showEvidenceBundleDialog: show }),
setShowGuidRegistryDialog: (show) => set({ showGuidRegistryDialog: show }),
setShowFileAssociationPrompt: (show) => set({ showFileAssociationPrompt: show }),

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

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

showGuidRegistryDialog is added to UI state, but closeTransientDialogs doesn’t include it in either the “nothing to close” guard or the set({ ... }) reset (see src/stores/ui-store.ts around lines 469–495). This means global “close transient dialogs” actions (e.g., Escape) may leave the GUID Registry dialog open. Consider adding showGuidRegistryDialog to that helper.

Copilot uses AI. Check for mistakes.
Comment thread src/stores/intune-store.ts Outdated
} from "../types/intune";

export type IntuneWorkspaceTab = "timeline" | "downloads" | "summary";
export type IntuneTimelineViewMode = "list" | "gantt";

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

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

The new view mode is labeled “Activity” in the UI (ViewModeToggle), but the type/value is "gantt" (IntuneTimelineViewMode = "list" | "gantt"). This mismatch makes the code harder to follow (and the spec doc also refers to “Gantt”). Consider renaming the mode value to "activity" (or renaming the UI label to “Gantt”) for consistency.

Suggested change
export type IntuneTimelineViewMode = "list" | "gantt";
export type IntuneTimelineViewMode = "list" | "activity";

Copilot uses AI. Check for mistakes.
- Fix sanitize_json in policy_parser.rs to iterate over chars instead of
  bytes, preventing corruption of non-ASCII app names
- Fix decode_script_body to recover bytes from FromUtf8Error instead of
  redundant base64 re-decode
- Add type="button" to ScriptCodeViewer copy button
- Rename timeline view mode from "gantt" to "activity" for clarity
- Add GUID tiebreaker to groupKey to prevent false merges of same-named apps
- Fix mergeGuidNameMap spread order to preserve existing entries

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@adamgell
adamgell merged commit 0f19193 into main Apr 2, 2026
6 of 7 checks passed
adamgell added a commit that referenced this pull request Apr 5, 2026
Resolve 4 outstanding Copilot review issues plus changelog updates:

- DiffConfigDialog: filter to log-only tabs, validate selections exist
- FileSidebar: filter directories and uncached files before merge
- session.ts: replace unsafe cast with field-by-field validation and defaults
- session-restore: load files individually for per-tab restore, restore
  active tab index and scroll positions

Update changelog with all unreleased features, PR references (#72, #78,
#79, #81, #82), and new Security section for CI permissions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
adamgell added a commit that referenced this pull request Apr 6, 2026
* feat(merge): add showMergeTabsDialog state to ui-store

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(merge): add pure merge logic — sorting, colors, correlation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(merge): add MergedTabState, merge/correlation actions to log store

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(merge): create MergeLegendBar with file toggles and correlation controls

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(merge): create MergeTabsDialog component

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(merge): add Merge Tabs button to toolbar

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(merge): wire MergeTabsDialog into AppShell

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(merge): show merged tab indicator in tab strip

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(merge): add file color borders, correlation highlights, and legend bar to log view

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(merge): add Merge into Timeline button to folder sidebar

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(merge): add correlated entries section to InfoPane

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add unified timeline spec and implementation plan

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add session save/restore and log diff specs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(session): add recentSessions state to ui-store

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(session): add compute_file_hash Rust command

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(diff): add normalization, pattern key, and classification logic

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(session): add session types, save, and restore logic

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(diff): add DiffState and diff actions to log store

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(session): add Save/Open Session menu items and handlers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(diff): add DiffConfigDialog, toolbar button, and ui-store state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(diff): create DiffView with side-by-side and unified modes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: wire DiffConfigDialog into AppShell, render DiffView, update changelog

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address code review — scroll sync, session restore, validation, performance

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Copilot PR review — accessibility, UTF-8, GUID casing, cleanup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add Graph API GUID resolution (Windows)

Introduce end-to-end Microsoft Graph integration to resolve Intune app GUIDs. Backend: new src-tauri/src/graph_api.rs implements WAM-based Windows auth, token caching, batch/single Graph requests, paginated app fetches and an in-memory GUID cache; adds fetch_all_apps and resolve_guids utilities. Frontend: new GraphApiTab settings UI to enable/sign-in/pre-populate cache, a startup hook to auto-connect and populate cache, status bar indicator for Graph status, and enhancements to the GUID registry dialog (tabs, filtering, publisher/type columns). Add lib/graph-registry.ts to convert GraphAppInfo to GuidRegistry entries and update types (GuidCategory, publisher). Also improve event_tracker to extract PolicyId from JSON payloads. Wire startup import in main.tsx and add ui-store state/handlers for graphApiEnabled and graphApiStatus.

* feat: add Microsoft Graph API integration for GUID resolution

Add opt-in Graph API support to resolve Intune app GUIDs to display
names using the device's existing Entra ID session via WAM (Web Account
Manager). No app registration required — uses the Microsoft Graph
PowerShell public client ID.

- WAM authentication with HWND interop for Win32 desktop apps
- Graph API client with batch resolution ($batch endpoint, 20 per request)
- Pre-populate cache button fetches all tenant apps in one call
- GraphApi source variant (highest confidence) in GuidRegistry
- Settings tab with opt-in toggle (off by default), consent warnings,
  and connection status display
- Automatic enrichment during Intune log analysis when enabled

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: resolve merge conflicts with Graph API integration, fix CI

- Fix analyze_intune_logs to use app.state() instead of cfg-gated parameter
  (Tauri generate_handler! can't handle #[cfg] on parameters)
- Gate Manager import to windows-only in both lib.rs and intune.rs
- Resolve merge conflicts in ui-store.ts (recentSessions + graphApi state)
- Resolve merge conflicts in GraphApiTab.tsx (keep buildGraphRegistryEntries)
- Fix GuidRegistryDialog aria-label to use entry.guid

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address PR #82 review — ID collisions, persist sessions, correlation refresh, diff close

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ci: force clean build to resolve stale cache

* fix: add missing script_body and parent_app_guid to RFC3339 test

The sysmon merge brought a new test (build_timeline_sorts_rfc3339_timestamps)
that was missing the two fields added to IntuneEvent in the AppWorkload PR.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* security: add explicit permissions to CI workflow

Restrict GITHUB_TOKEN to contents:read across all jobs to satisfy
the principle of least privilege. Resolves CodeQL alerts #1#3.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: gate sysmon module behind feature flag, fix clippy

- Add sysmon feature to Cargo.toml, include in full feature set
- Gate pub mod sysmon and analyze_sysmon_logs command with #[cfg(feature = "sysmon")]
- Gate sysmon commands mod with #[cfg(feature = "sysmon")]
- Add required-features = ["sysmon"] to sysmon_parser test
- Fix clippy needless_range_loop in event_tracker.rs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: resolve merge conflicts in graph_api.rs

Unresolved conflict markers from d065202 merge caused unclosed
delimiter errors on Windows CI builds. Kept the refactored
fetch_paginated helper that supports multiple Intune endpoints.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: resolve merge conflicts from upstream session/diff features

Merge Graph API integration (graphApiEnabled, graphApiStatus) with
upstream session save/restore (recentSessions) and diff features.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: implement live Windows Event Log queries

Implement EvtOpenChannelEnum, EvtQuery, EvtNext, EvtRender, and
EvtFormatMessage via Win32 API to query live event log channels.
Auto-loads Application, System, Security, and Setup on "This Computer".

- Raw FFI for channel enumeration (fixes NULL handle issue)
- Buffer retry for EvtRender with both Win32 and HRESULT error codes
- Rendered messages via EvtFormatMessage with publisher metadata cache
- XML string parsing for EventID, Level, Provider, TimeCreated, EventData
- Progressive channel loading with per-channel error handling
- Channel picker: Event Viewer-style tree (Windows Logs / App & Services)
- Resizable channel sidebar with drag handle
- Resizable detail pane with drag handle
- Arrow key navigation in timeline (Up/Down/Home/End)
- Progress bar during channel loading
- Status bar shows "Event Log" with channel/event counts
- Load button for querying additional checked channels
- Error messages surfaced to frontend via errorMessages field

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: live event log queries, Graph API scripts, parallel loading

Live Event Log:
- Implement Win32 EvtQuery/EvtRender/EvtFormatMessage for live channel queries
- Auto-load Application, System, Security, Setup in parallel
- Event Viewer-style nested tree sidebar (split on - and /)
- Resizable sidebar and detail pane with drag handles
- Arrow key navigation in timeline
- Progressive loading with spinner and elapsed time in status bar
- Refresh button to reload channels
- No event cap — loads all events on disk
- DevTools auto-open in debug builds

Graph API enhancements:
- Fetch remediation scripts, platform scripts, shell scripts
- GUID Registry tabbed view (All/Apps/Scripts/Remediations)
- Category and publisher columns
- Auto-connect on startup with status bar indicator
- PolicyId extraction for HealthScripts events

Fixes:
- HRESULT error code handling (low 16-bit extraction)
- EvtRender buffer retry for large events
- Status bar shows correct workspace labels
- Hide FileSidebar in Event Log workspace

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: gate devtools and progress events for cross-platform CI

- Gate open_devtools behind #[cfg(all(debug_assertions, desktop))]
- Gate EvtxQueryProgress, Serialize, Emitter behind #[cfg(target_os = "windows")]
- Fixes cargo check on Ubuntu CI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address remaining Copilot PR #82 review comments, update changelog

Resolve 4 outstanding Copilot review issues plus changelog updates:

- DiffConfigDialog: filter to log-only tabs, validate selections exist
- FileSidebar: filter directories and uncached files before merge
- session.ts: replace unsafe cast with field-by-field validation and defaults
- session-restore: load files individually for per-tab restore, restore
  active tab index and scroll positions

Update changelog with all unreleased features, PR references (#72, #78,
#79, #81, #82), and new Security section for CI permissions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add workspace registry design spec

Defines a pluggable workspace registry to replace 25+ scattered if/else
chains across 8 frontend files with centralized, type-safe definitions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add workspace registry implementation plan (Phase 1-2)

9 tasks covering foundation types, registry, and sysmon migration
as the template workspace.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(workspaces): add WorkspaceDefinition types

* feat(workspaces): add central workspace registry

* refactor(sysmon): move sysmon types to src/workspaces/sysmon/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(sysmon): move sysmon store to src/workspaces/sysmon/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(sysmon): move sysmon components to src/workspaces/sysmon/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(sysmon): move analysis progress hook to src/workspaces/sysmon/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(sysmon): extract SysmonSidebar to src/workspaces/sysmon/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(workspaces): register sysmon as first workspace definition

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: add workspace registry Phase 3 implementation plan

6 tasks: shim definitions, AppShell/FileSidebar/ui-store/Toolbar refactors

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(workspaces): add shim definitions for all workspaces

Creates shim WorkspaceDefinition objects for the 7 remaining workspaces
(log, intune, new-intune, dsregcmd, macos-diag, deployment, event-log),
exports LogSidebar/IntuneSidebar/DsregcmdSidebar from FileSidebar.tsx,
and registers all 8 workspaces in the workspace registry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(appshell): use workspace registry for component routing

Replace the 8-way if/else chain in renderWorkspace() with a registry
lookup + Suspense for all non-log workspaces. Log workspace inline
rendering (DiffView, RegistryViewer, folder overlay, InfoPane) is
preserved as a special case. TabStrip and FindBar conditionals now
consult workspace capabilities flags instead of hard-coding activeView.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(sidebar): use workspace registry for sidebar routing

Replace the ternary chain (isIntuneWorkspace / activeView === 'sysmon'
checks) with a registry lookup via getWorkspace(activeView).sidebar, and
drive the footer bar from capabilities.footerBar. Remove now-unused
SysmonSidebar direct import and isIntuneWorkspace import.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(ui-store): delegate platform gating to workspace registry

Remove WORKSPACE_PLATFORM_MAP constant and inline filter logic from
getAvailableWorkspaces(). Delegate to getRegistryWorkspaces() from
the workspace registry instead, mapping WorkspaceDefinition[] back
to WorkspaceId[] to preserve the existing public API surface.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(toolbar): use workspace registry for labels and file filters

Replace WORKSPACE_LABELS, getOpenFileDialogFilters(), and
getOpenActionLabels() with direct lookups from getWorkspace() in the
workspace registry, eliminating ~75 lines of duplicated metadata.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(dsregcmd): migrate workspace to src/workspaces/dsregcmd/

Move all dsregcmd-specific files into src/workspaces/dsregcmd/ to make
the workspace fully self-contained, following the same pattern as sysmon.

- git mv types/dsregcmd.ts → workspaces/dsregcmd/types.ts
- git mv stores/dsregcmd-store.ts → workspaces/dsregcmd/dsregcmd-store.ts
- git mv components/dsregcmd/* → workspaces/dsregcmd/ (7 files)
- Extract DsregcmdSidebar from FileSidebar.tsx into workspaces/dsregcmd/DsregcmdSidebar.tsx
- Update index.ts shim to use local ./ imports
- Update all external importers (Toolbar, StatusBar, EvidenceBundleDialog,
  commands.ts, dsregcmd-source.ts, dsregcmd-store.test.ts, event-log/index.ts)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(event-log): migrate workspace to src/workspaces/event-log/

Move types, store, and all 7 components out of src/types/, src/stores/,
and src/components/event-log-workspace/ into the unified workspace directory.
Update all internal imports to use relative paths within the workspace.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(macos-diag): migrate workspace to src/workspaces/macos-diag/

Move types, store, and all 9 components from their scattered locations
(src/types/, src/stores/, src/components/macos-diag/) into the unified
workspace directory src/workspaces/macos-diag/. Update all internal
and external import paths accordingly. TypeScript passes clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(deployment): migrate workspace to src/workspaces/deployment/

Moves deployment-store.ts and all 3 component files (DeploymentWorkspace,
DeploymentErrorCard, DeploymentSuccessTable) from their legacy locations into
src/workspaces/deployment/. Updates all import paths in the moved files,
the workspace index shim, StatusBar.tsx, and both dynamic imports in Toolbar.tsx.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(intune): migrate workspace to src/workspaces/intune/

Moves all 19 intune components, the intune store (1,095 lines), types,
hook, and test file from their scattered locations into the consolidated
src/workspaces/intune/ directory. Extracts IntuneSidebar from
FileSidebar.tsx into its own file with helpers inlined. Updates all
importers across layout, dialogs, hooks, and lib modules. Both the
intune and new-intune workspace shims now reference local workspace paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: remove empty component directories after workspace migration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: add .superpowers/ to gitignore, remove tracked artifacts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: extract SourceSummaryCard to shared component, consolidate getBaseName

- Create src/components/common/sidebar-primitives.tsx with SourceSummaryCard,
  SourceStatusNotice, SectionHeader, EmptyState, and SidebarActionButton
- Remove inline copies of all five components from FileSidebar, IntuneSidebar,
  DsregcmdSidebar, and SysmonSidebar; import from shared module instead
- Widen canonical getBaseName signature to string | null | undefined
- Remove exported getBaseName from log-store; import from file-paths instead
- Update all callers in log-source, dsregcmd-source, EvidenceBundleDialog,
  StatusBar, IntuneSidebar, SysmonSidebar, and FileSidebar to use file-paths
- Also removes local getDirectoryName copy from EvidenceBundleDialog

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(ui-store): replace getUiChromeStatus if/else chain with registry lookup

Add statusLabel to WorkspaceDefinition; set overrides on log, intune, and
new-intune workspaces. getUiChromeStatus now resolves labels via getWorkspace()
and branches only on capabilities.detailsPane, eliminating the 8-way if/else.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(toolbar): wire onOpenSource handlers and capability-based commandState

Move workspace-specific analysis logic (intune, dsregcmd, sysmon, deployment)
from Toolbar.tsx into each workspace's onOpenSource in its definition. Replace
the openSourceForWorkspace if/else chain with a registry lookup. Add knownSources
and tailing to WorkspaceCapabilities and use them in commandState instead of
hardcoded workspace ID checks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: update evtx-store import path after workspace migration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Copilot PR #84 review comments

- event-log: remove incorrect DsregcmdSidebar, fix file filters to EVTX
- deployment: handle file sources by analyzing parent directory
- types: move IntuneTimestampBounds to shared types to break circular dep

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants