Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ The app can scan this Mac directly or import an existing raw JSON report. Proces

### Current source features

The features below are included in v0.1.2 and the current source on `main`, including collection health, the comparison dashboard, snapshots, review summaries, and diagnostic-log explanations.
This section describes the current source checkout, not a guarantee about the downloaded release. The published v0.1.2 app is an earlier build: pending comparison/raw-evidence improvements, optional plain-English explanations, PDF/export corrections, and Camera field explanations are not included in that download. Until merged, these changes are available only in development checkouts and review branches; consult the release notes for the features shipped in each download.

- All supported `system_profiler` data types represented by the app’s current macOS toolchain
- A sidebar with eight subjects (Overview, Hardware, Storage, Network, Software, Security, Power, and Reports) and the What Changed? tool
Expand Down Expand Up @@ -229,12 +229,34 @@ Choose **Share › Report File** to save the scan data. The export review offers
- **Redacted report** replaces every reported scalar and record name, omits exact collection times, and removes standard-error text.
- **Full private report** preserves exact values and metadata for private analysis and future comparison.

A current scan can be compared with a compatible full report created from the same subject or full-report scope. Named records are matched independently of order; unnamed records and array elements are matched by position. Added, removed, and changed values identify structured differences, not their cause or security impact.
A current scan can be compared with a compatible full report created from the same subject or full-report scope, with each data type appearing only once. Top-level records and nested lists containing only object records use original unique names to match independently of order. Duplicate-name and unnamed records match by identical complete contents only when the value occurs once on each side. Non-unique candidates remain unresolved, with their original addresses accessible; unambiguous findings continue to be compared. A sole unnamed top-level section container, scalar lists, and mixed-type lists remain positional. Baseline and current source locations retain each report's actual top-level and nested indices, even when matched records move. Added findings have only a current location; removed findings have only a baseline location. The comparison sheet's search includes both locations. Added, removed, and changed values identify structured differences, not their cause or security impact.

Compare in **What Changed?**, under Tools in the sidebar. Pick a saved snapshot of the same report (or compare with the latest in one click), or choose a file: a full export from this app or raw `system_profiler -json` output. It then presents clear added, removed, and changed totals. Differences are grouped as **Review First**, **Worth Reviewing**, or **Informational** to guide triage—not to assign a threat level. Expand a difference for its baseline/current values, raw source location, and a plain-language explanation grounded in the app’s explanation catalog.
If either report's known command scope includes a data type that was never returned, comparison stops with the affected baseline/current data types and collection labels. Recollect missing sections or choose complete reports of the same scope; incomplete collection is not an addition/removal or a successful “no changes” result. A section that was explicitly returned with no records remains comparable. Raw JSON imports lack their original collection scope, so the app compares only their present sections without inventing missing-collection history.

Compare in **What Changed?**, under Tools in the sidebar. Pick a saved snapshot of the same report (or compare with the latest in one click), or choose a file: a full export from this app or raw `system_profiler -json` output. The same-Mac workflow checks existing Hardware overview identifiers; conflicts block comparison. Missing identifiers require confirmation and leave a persistent **Same Mac not verified** warning. Matching identifiers do not authenticate report contents. No additional identifiers are collected.

All confirmed differences are available by default, including potentially noisy values. The overview groups them as **Review First**, **Worth Reviewing**, or **Informational** to guide triage—not to assign a threat level. **Review Details** provides search and added/removed/changed filters, with visible, matching, total, and hidden-by-filter counts. Ambiguous record groups remain unresolved, with candidates from both reports accessible separately from confirmed change totals; no pairing is guessed.

Expand a difference or choose **Inspect baseline and current evidence** to keep the change list visible alongside a two-column evidence inspector. Each side shows its actual typed field/index address, value type, reconstructed structured JSON, and an exact excerpt from retained UTF-8 input, with surrounding record context available on demand. Literal keys containing dots or brackets remain distinct. Missing fields, reported nulls, source-resolution errors, and **Original source unavailable** are separate states. Original input bytes are held in owned memory only for the open report/comparison: they are not added to snapshots, exports, caches, or model requests. Older snapshots without retained input still provide structured evidence. Source excerpts preserve input formatting and escapes; reconstructed JSON is never labeled as original bytes. Input that cannot be addressed uniquely or uses an unsupported source encoding produces an explicit source error while structured evidence remains accessible. No source file is reopened by the inspector.

**Refresh Snapshots** reloads local history without changing the selected report or comparison. Loading failures are shown explicitly, and unreadable files are listed with a warning while readable, compatible full snapshots remain available. When no snapshot baseline is available, **Choose File…** opens the file picker directly; redacted snapshots cannot be compared.

For a readable document, choose **Share › Summary**. The app exports Markdown or PDF with At a glance, the values worth a look, the full explanations of any bookmarked findings, collection limits (including skipped sections), and an explicit privacy warning. It never folds raw JSON into this summary; use **Share › Report File** when raw evidence is required.

### Optional plain-English field guide (prototype)

Expand **About this field**, then choose **Explain in plain English** for supported hardware fields (chip, model name/identifier, installed memory, processor configuration, Boot ROM, OS loader) or battery cycle count. Apple Intelligence rewords only the app’s maintained, value-independent field description on this Mac. Reported values, identifiers, names, logs, and snapshots are not model input. Unknown/private/value-dependent fields are deliberately outside this prototype.

This requires macOS 26 or later and an available Apple Intelligence on-device model with English support. Unsupported Macs, disabled Intelligence, and models that are not ready get an explicit reason; the app’s existing collection, comparison, and maintained explanations continue to work without it. There is no cloud fallback, automatic retry, or settings change. Closing the sheet cancels its request; a 30-second deadline also requests cancellation, subject to the native model honoring it.

If unavailable, **Check availability** explicitly checks again; it does not enable Intelligence or download a model. A model becoming unavailable during a request keeps its specific reason. The app still supports macOS 13 and later, including Intel Macs; the optional framework is weak-linked and used only behind macOS 26 availability checks.

Generated prose is visibly labeled, may be inaccurate, and explains the field—not the reported value or the cause of a change. It never replaces curated text, changes finding status, or enters reports, snapshots, or exports. Review it against the maintained explanation and raw source. Developers can opt into the real native availability/generation check using `TEST_RUNNER_SYSTEM_PROFILER_NATIVE_EXPLANATION_TEST=1` with the native test command; an unavailable model verifies refusal, not successful generation.

To verify a build with the on-device integration completely excluded, add `SWIFT_ACTIVE_COMPILATION_CONDITIONS='$(inherited) SYSTEM_PROFILER_WITHOUT_ON_DEVICE_MODEL'` to the native build/test command and use a separate derived-data directory. This build makes no FoundationModels requests and shows an explicit build-without-framework reason. Testing it on a newer Mac is not a substitute for running the normal build on older macOS or physical Intel hardware.

Use the [older Intel Mac compatibility test guide](docs/compatibility-testing.md) for candidate verification, synthetic import/comparison fixtures, unavailable-state checks, safe snapshot isolation, and a results checklist. Xcode is not required to run the prebuilt test candidate.

## Build from source

Requirements:
Expand Down
32 changes: 32 additions & 0 deletions SystemProfilerExplorer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@
objects = {

/* Begin PBXBuildFile section */
F1E1D2A301004AB0920A0001 /* OriginalReportSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1E1D2A301004AB0920B0001 /* OriginalReportSource.swift */; };
F1E1D2A301004AB0910A0001 /* ComparisonRawEvidence.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1E1D2A301004AB0910B0001 /* ComparisonRawEvidence.swift */; };
F1E1D2A301004AB0910A0002 /* ComparisonRawEvidenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1E1D2A301004AB0910B0002 /* ComparisonRawEvidenceView.swift */; };
F1E1D2A301004AB0910A0003 /* ComparisonRawEvidenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1E1D2A301004AB0910B0003 /* ComparisonRawEvidenceTests.swift */; };
F1E1D2A301004AB0900A0003 /* OnDevicePlainEnglish.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1E1D2A301004AB0900B0003 /* OnDevicePlainEnglish.swift */; };
F1E1D2A301004AB0900A0004 /* PlainEnglishExplanationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1E1D2A301004AB0900B0004 /* PlainEnglishExplanationView.swift */; };
F1E1D2A301004AB0900A0001 /* PlainEnglishExplanation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1E1D2A301004AB0900B0001 /* PlainEnglishExplanation.swift */; };
F1E1D2A301004AB0900A0002 /* PlainEnglishExplanationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1E1D2A301004AB0900B0002 /* PlainEnglishExplanationTests.swift */; };
C35C203F766D456799E8BF1C /* AboutAppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A235CD75E0DA42F5BCFF5847 /* AboutAppView.swift */; };
007AF890AD2A2E150FC2FF77 /* PowerExplanationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AF48C92BB49A1D7C2BF3D06 /* PowerExplanationTests.swift */; };
06011960E1B3DC38DFF2638C /* SecureElementAndSmartCardExplanations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 433F05B4C4BA2EEF1C3E65B0 /* SecureElementAndSmartCardExplanations.swift */; };
Expand Down Expand Up @@ -120,6 +128,14 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
F1E1D2A301004AB0920B0001 /* OriginalReportSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OriginalReportSource.swift; sourceTree = "<group>"; };
F1E1D2A301004AB0910B0001 /* ComparisonRawEvidence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComparisonRawEvidence.swift; sourceTree = "<group>"; };
F1E1D2A301004AB0910B0002 /* ComparisonRawEvidenceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComparisonRawEvidenceView.swift; sourceTree = "<group>"; };
F1E1D2A301004AB0910B0003 /* ComparisonRawEvidenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComparisonRawEvidenceTests.swift; sourceTree = "<group>"; };
F1E1D2A301004AB0900B0003 /* OnDevicePlainEnglish.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnDevicePlainEnglish.swift; sourceTree = "<group>"; };
F1E1D2A301004AB0900B0004 /* PlainEnglishExplanationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlainEnglishExplanationView.swift; sourceTree = "<group>"; };
F1E1D2A301004AB0900B0001 /* PlainEnglishExplanation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlainEnglishExplanation.swift; sourceTree = "<group>"; };
F1E1D2A301004AB0900B0002 /* PlainEnglishExplanationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlainEnglishExplanationTests.swift; sourceTree = "<group>"; };
A235CD75E0DA42F5BCFF5847 /* AboutAppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutAppView.swift; sourceTree = "<group>"; };
01565FC67C66F8D63C14F3FD /* SystemProfilerExplorerApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemProfilerExplorerApp.swift; sourceTree = "<group>"; };
04216053FB73986A00A03A57 /* ReportComparison.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportComparison.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -229,6 +245,7 @@
isa = PBXGroup;
children = (
CEE75C08C482A71C55927F92 /* SystemProfilerParser.swift */,
F1E1D2A301004AB0920B0001 /* OriginalReportSource.swift */,
);
path = Parsing;
sourceTree = "<group>";
Expand Down Expand Up @@ -293,6 +310,8 @@
387CBF80EA9183D8E5CB8111 /* SystemProfilerExplorerTests */ = {
isa = PBXGroup;
children = (
F1E1D2A301004AB0910B0003 /* ComparisonRawEvidenceTests.swift */,
F1E1D2A301004AB0900B0002 /* PlainEnglishExplanationTests.swift */,
A841B78A28F111C12390310A /* ValueCatalogTests.swift */,
C16CF4E58F995FDE34AE3D09 /* ValuePanelTests.swift */,
5E4302147D18912C4D6C9E8F /* AdditionalExplanationCoverageTests.swift */,
Expand Down Expand Up @@ -387,6 +406,7 @@
84BB818C260D375773C51F1F /* Comparison */ = {
isa = PBXGroup;
children = (
F1E1D2A301004AB0910B0001 /* ComparisonRawEvidence.swift */,
6460BE296C1F7F744F9F12D3 /* ReportChangeInsight.swift */,
04216053FB73986A00A03A57 /* ReportComparison.swift */,
);
Expand Down Expand Up @@ -422,6 +442,8 @@
8D88B38D8A885854AFCD1A21 /* Explanations */ = {
isa = PBXGroup;
children = (
F1E1D2A301004AB0900B0003 /* OnDevicePlainEnglish.swift */,
F1E1D2A301004AB0900B0001 /* PlainEnglishExplanation.swift */,
D67584376B2445EF1DF746DE /* AccessibilityExplanations.swift */,
895BF724C185EB42ECC0960C /* AdditionalDataTypeExplanations.swift */,
78AF3F713C4A791BA1C636FA /* ContextualFieldExplanations.swift */,
Expand Down Expand Up @@ -463,6 +485,7 @@
A1F533BE0E9ECB4C82C46B37 /* Scan */ = {
isa = PBXGroup;
children = (
F1E1D2A301004AB0900B0004 /* PlainEnglishExplanationView.swift */,
FCB5B24DFE76BB7DE34B6878 /* DiagnosticLogView.swift */,
ADBD375703B00F7FFCFA2A47 /* ProfileReportView.swift */,
4A51AB239BF7150A0E3357EE /* ScanState.swift */,
Expand Down Expand Up @@ -491,6 +514,7 @@
BF685168274C6F83C67D2CED /* Comparison */ = {
isa = PBXGroup;
children = (
F1E1D2A301004AB0910B0002 /* ComparisonRawEvidenceView.swift */,
C19D421A0C7E053E97CA1F4B /* ReportComparisonView.swift */,
);
path = Comparison;
Expand Down Expand Up @@ -611,6 +635,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F1E1D2A301004AB0910A0003 /* ComparisonRawEvidenceTests.swift in Sources */,
F1E1D2A301004AB0900A0002 /* PlainEnglishExplanationTests.swift in Sources */,
B89D0EC021ABEEDFA1DE36B0 /* ValueCatalogTests.swift in Sources */,
B2C53C0C964281379BF8AEB3 /* ValuePanelTests.swift in Sources */,
41A352BEAE8240924801481C /* AdditionalExplanationCoverageTests.swift in Sources */,
Expand Down Expand Up @@ -646,6 +672,11 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F1E1D2A301004AB0910A0001 /* ComparisonRawEvidence.swift in Sources */,
F1E1D2A301004AB0910A0002 /* ComparisonRawEvidenceView.swift in Sources */,
F1E1D2A301004AB0900A0003 /* OnDevicePlainEnglish.swift in Sources */,
F1E1D2A301004AB0900A0004 /* PlainEnglishExplanationView.swift in Sources */,
F1E1D2A301004AB0900A0001 /* PlainEnglishExplanation.swift in Sources */,
C35C203F766D456799E8BF1C /* AboutAppView.swift in Sources */,
CD175DDDEB38931B4B1ECC72 /* ExtensionValueRules.swift in Sources */,
D0EB1B42212523FE20AEC875 /* ValuePanel.swift in Sources */,
Expand Down Expand Up @@ -706,6 +737,7 @@
B321B15EADFD1D966F559FF9 /* SystemProfilerDataType.swift in Sources */,
CF4AE65C768B9635443AFAC8 /* SystemProfilerExplorerApp.swift in Sources */,
4CF762F76B57796EE1586919 /* SystemProfilerParser.swift in Sources */,
F1E1D2A301004AB0920A0001 /* OriginalReportSource.swift in Sources */,
F35BDA59E1D813C22A89A2DE /* SystemProfilerReport.swift in Sources */,
4D38004930CA86BE1DF0A7D2 /* SystemProfilerRequest.swift in Sources */,
A1DAD36C9721BD5C2DA79631 /* SystemReviewPDF.swift in Sources */,
Expand Down
Loading
Loading