@salesforce/afv-skills 1.54.0 → 1.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/package.json +1 -1
  2. package/skills/dx-org-analyze/README.md +310 -0
  3. package/skills/dx-org-analyze/SKILL.md +261 -0
  4. package/skills/dx-org-analyze/references/collection-details.md +88 -0
  5. package/skills/dx-org-analyze/references/report-format.md +67 -0
  6. package/skills/dx-org-analyze/scripts/collect_org_data.py +823 -0
  7. package/skills/dx-org-analyze/scripts/compute_diff.py +1103 -0
  8. package/skills/dx-org-analyze/scripts/introspect_org.py +436 -0
  9. package/skills/dx-org-analyze/tests/README.md +46 -0
  10. package/skills/dx-org-analyze/tests/fixtures/org_a.json +76 -0
  11. package/skills/dx-org-analyze/tests/fixtures/org_b.json +70 -0
  12. package/skills/dx-org-analyze/tests/test_compute_diff.sh +284 -0
  13. package/skills/dx-org-analyze/tests/test_consistency.sh +428 -0
  14. package/skills/experience-design-validate/SKILL.md +163 -0
  15. package/skills/experience-design-validate/references/ai.md +54 -0
  16. package/skills/experience-design-validate/references/components.md +61 -0
  17. package/skills/experience-design-validate/references/craft.md +158 -0
  18. package/skills/experience-design-validate/references/data.md +59 -0
  19. package/skills/experience-design-validate/references/forms-flows.md +57 -0
  20. package/skills/experience-design-validate/references/interaction.md +53 -0
  21. package/skills/experience-design-validate/references/navigation.md +51 -0
  22. package/skills/experience-design-validate/references/performance.md +56 -0
  23. package/skills/experience-design-validate/references/records.md +60 -0
  24. package/skills/experience-design-validate/references/responsive.md +48 -0
  25. package/skills/experience-design-validate/references/scoring-rubric.md +257 -0
  26. package/skills/experience-design-validate/references/state.md +53 -0
  27. package/skills/experience-design-validate/references/trust.md +47 -0
  28. package/skills/experience-design-validate/references/usability.md +35 -0
  29. package/skills/experience-design-validate/references/visual-system.md +183 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/afv-skills",
3
- "version": "1.54.0",
3
+ "version": "1.55.0",
4
4
  "description": "Salesforce skills for Agentforce Vibes",
5
5
  "license": "CC-BY-NC-4.0",
6
6
  "files": [
@@ -0,0 +1,310 @@
1
+ # dx-org-analyze
2
+
3
+ Compare two Salesforce orgs and produce a comprehensive comparison report covering metadata, permissions, org settings, limits, profiles, installed packages, licenses, system permissions, and content-level code differences. Also supports single-org analysis via introspect mode.
4
+
5
+ ## Prerequisites
6
+
7
+ - **Salesforce CLI** (`sf`) installed and authenticated to both orgs
8
+ - Admin-level access on both orgs (needed for Tooling API queries)
9
+ - Both orgs authenticated via `sf org login web` or `sf org login jwt`
10
+ - Python 3.10+
11
+
12
+ ## Getting Started
13
+
14
+ ### 1. Authenticate your orgs
15
+
16
+ ```bash
17
+ # Authenticate via browser (interactive — most common)
18
+ sf org login web --alias my-dev-org
19
+ sf org login web --alias my-prod-org
20
+
21
+ # Via JWT for CI/automation
22
+ sf org login jwt --alias my-org --client-id <ID> --jwt-key-file <key> --username <user>
23
+
24
+ # Via access token (for orgs that don't support the standard connected app)
25
+ sf org login access-token --instance-url https://myorg.my.salesforce.com --alias my-org
26
+ ```
27
+
28
+ ### 2. Verify connectivity
29
+
30
+ ```bash
31
+ sf org list # Shows all authenticated orgs
32
+ ```
33
+
34
+ ### 3. Run a comparison (two orgs)
35
+
36
+ Invoke the skill and follow the prompts — it will show your authenticated orgs and ask you to pick a source and target.
37
+
38
+ ```yaml
39
+ Skill: dx-org-analyze
40
+ Request: "Compare my sandbox against production"
41
+ ```
42
+
43
+ ### 4. Run an analysis (single org)
44
+
45
+ For a single-org analysis, invoke the skill with one org. It will collect data and produce an Org Analysis Report covering metadata inventory, org settings, limits, packages, licenses, system permissions, and deep data.
46
+
47
+ ```yaml
48
+ Skill: dx-org-analyze
49
+ Request: "Introspect my production org"
50
+ ```
51
+
52
+ ## How It Works
53
+
54
+ ### Comparison (two orgs)
55
+
56
+ 1. **List authenticated orgs** — Shows orgs from `sf org list`
57
+ 2. **Select source and target** — Source is the reference; target is compared against it
58
+ 3. **Collect data** — Queries metadata via SF CLI and Tooling API, org settings, limits, packages, licenses, system permissions, and deep data (per org, in parallel)
59
+ 4. **Compute differences** — Set comparisons, field-level diffs, body-hash diffs, CRUD permission diffs, metadata enrichment with content-level differences
60
+ 5. **Generate Org Comparison Report** — Drift Score → Summary Statistics → Detailed Differences → Package Components → Permissions → Org Values & Limits → Licenses → Deep Data
61
+
62
+ ### Analysis (single org)
63
+
64
+ 1. **Validate connectivity** — Confirms the org is reachable
65
+ 2. **Collect data** — Same collection as comparison mode, but for one org
66
+ 3. **Generate Org Analysis Report** — Metadata Inventory → Installed Packages → Org Settings → Org Limits → Licenses → System Permissions → Deep Data
67
+
68
+ ## Report Structure
69
+
70
+ ### Org Comparison Report (two orgs)
71
+
72
+ 1. **Drift Score** — Overall weighted score (0–100%) with severity level
73
+ 2. **Summary Statistics** — Counts per metadata type (with Identical and Different columns enriched from deep data)
74
+ 3. **Metadata Components by Type** — Full breakdown of only-in-A, only-in-B, shared per type
75
+ 4. **Profiles** — Shared, source-only, target-only profiles
76
+ 5. **Installed Packages** — Version comparison, only-in-A, only-in-B
77
+ 6. **Package Components** — Namespaced components grouped by namespace; non-namespaced packages flagged separately
78
+ 7. **Org Permissions** — Boolean enabled/disabled diffs by category, plus non-boolean value diffs
79
+ 8. **System Permissions** — PermissionsXxx fields on PermissionSet, per-set diffs
80
+ 9. **Org Values & Limits** — Grouped by Identity, Storage, API, Feature Limits, Other
81
+ 10. **Licenses** — User licenses, permission set licenses, package licenses with quantity diffs
82
+ 11. **Deep Data** — Content-level diffs for Apex, Flows, Validation Rules, Custom Fields, etc.
83
+
84
+ ### Org Analysis Report (single org)
85
+
86
+ 1. **Summary** — Org identity, edition, metadata type and component counts
87
+ 2. **Metadata Inventory** — Component counts per metadata type, org-native vs namespaced
88
+ 3. **Installed Packages** — Package name, namespace, version
89
+ 4. **Org Settings** — Enabled/disabled/non-boolean settings grouped by category
90
+ 5. **Org Limits** — API, storage, feature limits grouped by category
91
+ 6. **Licenses** — User licenses, permission set licenses, package licenses with usage counts
92
+ 7. **System Permissions** — Permission sets and their enabled PermissionsXxx fields
93
+ 8. **Deep Data** — Record counts for Apex classes, flows, custom fields, validation rules, etc.
94
+ 9. **Collection Warnings** — Any non-fatal issues encountered during data collection
95
+
96
+ ### Drift Score Formula
97
+
98
+ ```text
99
+ Overall = 60% × Metadata Drift + 30% × Permission Drift + 10% × Profile Drift
100
+ ```
101
+
102
+ | Range | Level | Meaning |
103
+ |-------|-------|---------|
104
+ | 0–10% | LOW | Nearly identical orgs |
105
+ | 11–30% | MODERATE | Some features differ |
106
+ | 31–60% | HIGH | Significant divergence |
107
+ | 61–100% | CRITICAL | Fundamentally different orgs |
108
+
109
+ ## What's Compared
110
+
111
+ ### Metadata (Name-Level)
112
+
113
+ All types discovered via `sf org list metadata-types` (typically 100+ types).
114
+ Falls back to Tooling API queries if CLI metadata commands are unavailable.
115
+ Namespaced (managed-package) components are excluded from the summary table and listed separately under Package Components.
116
+
117
+ ### Deep Data (Content-Level via Tooling API)
118
+
119
+ | Category | Comparison Depth |
120
+ |----------|-----------------|
121
+ | Apex Classes | Body hash, API version, LOC, status for shared classes |
122
+ | Apex Triggers | Body hash comparison for shared triggers |
123
+ | Custom Fields | Per-object field sets + length/precision/scale changes |
124
+ | Validation Rules | Active/inactive state differences + per-org-only rules |
125
+ | Flows | MasterLabel, process type, API version, status |
126
+ | Named Credentials | Endpoint URL comparison |
127
+ | Connected Apps | DeveloperName presence comparison |
128
+ | Custom Metadata Types | Presence comparison |
129
+
130
+ Deep data differences are mapped back to the summary statistics table via metadata enrichment, so the Identical/Different columns reflect actual content-level changes, not just name-level overlap.
131
+
132
+ ### Org-Level
133
+
134
+ | Category | Source | Depth |
135
+ |----------|--------|-------|
136
+ | Org Settings | Tooling API `OrganizationSettingsDetail` (fallback: `SecurityHealthCheckRisks`) | Enabled/disabled feature comparison + non-boolean value diffs |
137
+ | Org Limits | REST `/limits/` endpoint | Max values for API, storage, features |
138
+ | Profiles | Tooling API | Name-level (shared/Source-only/Target-only) |
139
+ | System Permissions | Tooling API `PermissionSet` | PermissionsXxx field comparison per permission set |
140
+ | Installed Packages | Tooling API `InstalledSubscriberPackage` | Name + namespace + version comparison |
141
+ | Package Components | Metadata listing | Namespaced components grouped by namespace |
142
+ | Licenses | SOQL | User, Permission Set, and Package license counts |
143
+
144
+ ## Scripts
145
+
146
+ ### `collect_org_data.py`
147
+
148
+ Collects all data from a single org into a **split directory** with per-category JSON files.
149
+
150
+ ```bash
151
+ python3 ./scripts/collect_org_data.py \
152
+ --org-alias my-dev-org \
153
+ --output /tmp/org-data
154
+ ```
155
+
156
+ | Flag | Purpose |
157
+ |------|---------|
158
+ | `--org-alias` | SF CLI alias or username for the org (required) |
159
+ | `--output` | Output directory path (required) |
160
+ | `--skip-deep-data` | Skip Tooling API content-level queries (faster, less detail) |
161
+
162
+ **Output directory structure:**
163
+
164
+ ```text
165
+ /tmp/org-data/
166
+ manifest.json # Lists category files
167
+ identity.json # Org ID, name, edition, errors
168
+ metadata.json # Metadata types and components
169
+ permissions.json # Org settings (OrganizationSettingsDetail)
170
+ org_values.json # Org limits
171
+ packages.json # Installed packages
172
+ licenses.json # User, PSL, and package licenses
173
+ system_permissions.json # PermissionsXxx on PermissionSet
174
+ deep_data.json # Apex bodies, Flow state, etc.
175
+ combined.json # All-in-one (backward compatibility)
176
+ ```
177
+
178
+ Split files enable parallel agent access — each agent loads only the section it needs.
179
+
180
+ **SOQL pagination:** Queries returning >2000 records are automatically paginated via `nextRecordsUrl`.
181
+
182
+ **Parallelized collection:** Metadata listing uses 12 workers, deep data queries use 8 workers via `ThreadPoolExecutor`.
183
+
184
+ Exit codes: `0` success, `1` fatal error, `2` session expired (re-auth and retry).
185
+
186
+ ### `compute_diff.py`
187
+
188
+ Computes the comparison between two collected org data sets.
189
+ Accepts either a split directory or a single JSON file for each org.
190
+
191
+ ```bash
192
+ python3 ./scripts/compute_diff.py \
193
+ --org-a /tmp/source \
194
+ --org-b /tmp/target \
195
+ --output /tmp/org-comparison \
196
+ --format both \
197
+ --org-a-label "Source" \
198
+ --org-b-label "Target"
199
+ ```
200
+
201
+ | Flag | Purpose |
202
+ |------|---------|
203
+ | `--org-a` | Path to org A data directory or JSON file (required) |
204
+ | `--org-b` | Path to org B data directory or JSON file (required) |
205
+ | `--output` | Output path prefix (required) |
206
+ | `--format` | `markdown`, `json`, or `both` (default: `both`) |
207
+ | `--org-a-label` | Display name for org A in the report (default: "Source") |
208
+ | `--org-b-label` | Display name for org B in the report (default: "Target") |
209
+ | `--show-shared` | Include shared components in the detailed breakdown |
210
+
211
+ Outputs `<prefix>.json` (structured data) and `<prefix>.md` (formatted report).
212
+
213
+ ### `introspect_org.py`
214
+
215
+ Generates a single-org introspection report from a previously collected org data directory (output of `collect_org_data.py`). Covers metadata inventory, org settings, org limits, installed packages, licenses, system permissions, and deep data.
216
+
217
+ ```bash
218
+ python3 ./scripts/introspect_org.py \
219
+ --org /tmp/org-data \
220
+ --output /tmp/report \
221
+ --format both \
222
+ --label "My Production Org"
223
+ ```
224
+
225
+ | Flag | Purpose |
226
+ |------|---------|
227
+ | `--org` | Path to collected org data directory or JSON file (required) |
228
+ | `--output` | Output path prefix (required) |
229
+ | `--format` | `markdown`, `json`, or `both` (default: `both`) |
230
+ | `--label` | Display name for the org in the report (default: org alias or directory name) |
231
+
232
+ Outputs `<prefix>.json` (structured data) and/or `<prefix>.md` (formatted report) depending on `--format`.
233
+
234
+ ## Security
235
+
236
+ - **Read-only** — Never deploys, modifies, or deletes anything in either org.
237
+ - **SF CLI auth only** — All authentication through SF CLI. No raw credentials, session IDs, or access tokens accepted.
238
+
239
+ ## Use Cases
240
+
241
+ ### Compare sandbox against production
242
+
243
+ Ensure sandbox is in sync before deploying changes.
244
+
245
+ ### Audit org configuration drift
246
+
247
+ Detect unexpected changes between environments.
248
+
249
+ ### Compare dev org against customer org
250
+
251
+ Identify what metadata/configuration your package adds or modifies.
252
+
253
+ ### Analyze a single org
254
+
255
+ Inventory all metadata, settings, permissions, packages, and licenses in an org before making changes or onboarding a new environment.
256
+
257
+ ## Known Limitations
258
+
259
+ - Deep data body queries may timeout on large orgs (use `--skip-deep-data` to work around)
260
+ - Edition differences (DE vs EE) create inherent drift that isn't configuration drift
261
+ - Deep data comparison sections are capped at 50 entries in the report (full data in JSON output)
262
+ - `OrganizationSettingsDetail` availability varies by org edition — falls back to `SecurityHealthCheckRisks` which preserves raw values but may include non-boolean settings
263
+ - Non-namespaced package components (e.g. unlocked 2GP) cannot be distinguished from org-native metadata
264
+
265
+ ## Tips
266
+
267
+ - **Edition-aware:** DE vs EE orgs will always show permission/profile drift — this is expected
268
+ - **Session expiry:** If your session expires, re-run `sf org login web` and retry
269
+ - **Large orgs:** Use `--skip-deep-data` for a faster initial pass; run full deep data on targeted follow-ups
270
+ - **Custom labels:** Use `--org-a-label` / `--org-b-label` to make the report human-readable
271
+ - **Split files:** For large orgs, agents can load individual category files in parallel instead of the full combined.json
272
+
273
+ ## Skill Structure
274
+
275
+ ```text
276
+ dx-org-analyze/
277
+ ├── SKILL.md # Workflow, rules, and reference index
278
+ ├── scripts/
279
+ │ ├── collect_org_data.py # Per-org data collection (SF CLI + Tooling API)
280
+ │ ├── compute_diff.py # Diff computation, drift scoring, report generation
281
+ │ └── introspect_org.py # Single-org introspection report generation
282
+ ├── references/
283
+ │ ├── collection-details.md # What the collection script gathers and how
284
+ │ └── report-format.md # Drift score formula and report sections
285
+ └── tests/
286
+ ├── test_consistency.sh # SKILL.md and script structural tests
287
+ ├── test_compute_diff.sh # Diff computation tests with fixture data
288
+ ├── fixtures/ # Sample org data for testing
289
+ │ ├── org_a.json
290
+ │ └── org_b.json
291
+ └── README.md # Test documentation
292
+ ```
293
+
294
+ ## Tests
295
+
296
+ ```bash
297
+ # Run all consistency tests (no credentials needed)
298
+ bash tests/test_consistency.sh
299
+ # Expected: 74 passed, 0 failed
300
+ ```
301
+
302
+ ## Contributing
303
+
304
+ To extend the comparison categories, update:
305
+
306
+ 1. `scripts/collect_org_data.py` — Add a query to `collect_deep_data()` or a new collection function
307
+ 2. `scripts/compute_diff.py` — Add comparison logic (e.g. in `diff_deep_data()`) and rendering in `render_markdown()`; if adding a new enrichable type, update `enrich_metadata_with_deep_diffs()`
308
+ 3. `SKILL.md` — Update step descriptions if the workflow changes
309
+ 4. `tests/test_consistency.sh` — Add assertions for new sections
310
+
@@ -0,0 +1,261 @@
1
+ ---
2
+ name: dx-org-analyze
3
+ description: "Compare two Salesforce orgs side-by-side and produce a comparison report with drift score, or analyze a single org to produce an inventory covering metadata components, org permissions, system permissions, profiles, installed packages, licenses, and org limits. Use this skill when the user wants to compare orgs, diff orgs, audit configuration drift, find what changed between two environments, compare sandbox against production, or analyze a single org. Trigger phrases include: 'compare orgs', 'compare these 2 orgs', 'compare my orgs', 'diff orgs', 'org diff', 'what is different between these orgs', 'compare metadata', 'org comparison', 'audit org differences', 'compare sandbox to production', 'introspect org', 'org inventory', 'what is in my org', 'analyze org', 'org analysis', 'analyze my org', 'analyze my Salesforce org'. Do NOT use for retrieving metadata (use platform-metadata-retrieve), deploying metadata (use platform-metadata-deploy), or switching orgs (use dx-org-switch)."
4
+ allowed-tools: Bash Read
5
+ metadata:
6
+ version: "1.0"
7
+ minApiVersion: "62.0"
8
+ domains: ["Developer Experience"]
9
+ relatedSkills:
10
+ - "dx-org-manage"
11
+ - "dx-org-switch"
12
+ - "platform-metadata-deploy"
13
+ - "platform-metadata-retrieve"
14
+ cliTools:
15
+ - tool: ["python3"]
16
+ semver: ">=3.10"
17
+ - tool: ["sf"]
18
+ semver: ">=2.0.0"
19
+ ---
20
+
21
+ ## Tool Restrictions
22
+
23
+ **Use ONLY the Bash tool** to execute all `sf` CLI commands and Python scripts. Always include `--json` for `sf` commands. Do NOT use `mcp__salesforce_dx__*` tools.
24
+
25
+ **Output artifacts for eval/testing:** When an output directory is available, write results there. After executing the skill: (1) if the user specified an output path, write there immediately; (2) otherwise run `[ -d force-app/main/adk-eval-output/ ] && echo 'force-app/main/adk-eval-output'` to detect the eval directory; (3) write the full JSON result to `<output-dir>/dx-org-analyze-result.json` and the markdown report to `<output-dir>/dx-org-analyze-result.md`.
26
+
27
+ ---
28
+
29
+ ## Data Access Hierarchy
30
+
31
+ When querying org data, always follow this priority order:
32
+
33
+ 1. **SF CLI commands** — Preferred. Use `sf org list metadata-types`, `sf org list metadata`, `sf org display`, `sf data query`, `sf limits api display`, etc.
34
+ 2. **Direct REST/Tooling API calls** — Last resort, only when SF CLI cannot provide the data.
35
+
36
+ Never bypass this hierarchy. If an SF CLI command exists for the operation, use it even if a direct API call would be simpler.
37
+
38
+ ---
39
+
40
+ ## Authentication Rules
41
+
42
+ - **All authentication MUST go through SF CLI** (`sf org login web`, `sf org login jwt`, `sf org login access-token`).
43
+ - Never accept raw credentials (username + password), session IDs, or access tokens directly from the user.
44
+ - The collection script obtains its access token exclusively via `sf org display --json`.
45
+
46
+ ---
47
+
48
+ ## Workflow
49
+
50
+ ### Step 0: List Authenticated Orgs
51
+
52
+ Run this command to discover all authenticated orgs:
53
+
54
+ ```bash
55
+ sf org list --json --skip-connection-status
56
+ ```
57
+
58
+ Parse the JSON output. Collect orgs from **all buckets** (`devHubs`, `nonScratchOrgs`, `scratchOrgs`, `sandboxes`, `other`). Present authenticated orgs in a readable table:
59
+
60
+ | # | Alias | Username | Instance URL | Org ID | Type |
61
+ |---|-------|----------|--------------|--------|------|
62
+
63
+ If fewer than 2 orgs are authenticated but at least 1 is available, offer the **single-org introspect** mode (see Introspect Workflow below). If no orgs are authenticated, STOP and advise:
64
+ > You need at least 1 authenticated org. Run `sf org login web --alias <name>` to authenticate.
65
+
66
+ If the user explicitly requests a single-org introspection or inventory, use the **Introspect Workflow** regardless of how many orgs are available.
67
+
68
+ ### Step 1: User Selects Two Orgs
69
+
70
+ Ask the user to select two orgs from the list. Both must be explicitly named — do NOT allow implicit/default orgs.
71
+
72
+ > Select two orgs to compare. Which is the **source** (reference/expected state)? Which is the **target** (to compare against)?
73
+
74
+ Accept: alias, username, or number from the list. Resolve each selection to a concrete **username**. If an org lacks an alias, prompt the user to assign one. Confirm:
75
+
76
+ > Comparing:
77
+ > - **Source**: `<alias>` (`<username>`)
78
+ > - **Target**: `<alias>` (`<username>`)
79
+
80
+ ### Step 2: Validate Connectivity
81
+
82
+ For each org, confirm reachability with a lightweight query that does not expose secrets:
83
+
84
+ ```bash
85
+ sf data query --target-org <alias-or-username> --query "SELECT Id FROM Organization LIMIT 1" --json
86
+ ```
87
+
88
+ If the query succeeds (exit 0 and a record is returned), the org is connected. If it fails with `INVALID_SESSION_ID` or auth errors:
89
+ ```bash
90
+ sf org login web --alias <alias>
91
+ ```
92
+
93
+ ### Step 3: Collect Data (per org)
94
+
95
+ Generate a unique run ID and run the collection script for each org:
96
+
97
+ ```bash
98
+ RUN_ID=$(date +%Y%m%d-%H%M%S)
99
+
100
+ python3 ./scripts/collect_org_data.py \
101
+ --org-alias "$SOURCE_ORG" \
102
+ --output /tmp/dx-org-comparison-${RUN_ID}-source
103
+
104
+ python3 ./scripts/collect_org_data.py \
105
+ --org-alias "$TARGET_ORG" \
106
+ --output /tmp/dx-org-comparison-${RUN_ID}-target
107
+ ```
108
+
109
+ **Exit codes:** `0` = success, `1` = fatal error (report stderr to user), `2` = session expired (re-authenticate Step 2 and retry).
110
+
111
+ For details on what the collection script gathers, see `references/collection-details.md`.
112
+
113
+ ### Step 4: Compute Diff and Report
114
+
115
+ ```bash
116
+ python3 ./scripts/compute_diff.py \
117
+ --org-a /tmp/dx-org-comparison-${RUN_ID}-source \
118
+ --org-b /tmp/dx-org-comparison-${RUN_ID}-target \
119
+ --output /tmp/dx-org-comparison-${RUN_ID} \
120
+ --format both \
121
+ --org-a-label "Source" \
122
+ --org-b-label "Target"
123
+ ```
124
+
125
+ Use `--show-shared` if the user wants shared components listed in detail.
126
+
127
+ ### Step 5: Present Results
128
+
129
+ Read `/tmp/dx-org-comparison-${RUN_ID}.md` and present to the user. If the user asks follow-up questions, use `/tmp/dx-org-comparison-${RUN_ID}.json` for data lookups. Then resolve the output directory and copy both files there:
130
+
131
+ ```bash
132
+ OUTPUT_DIR=""
133
+ if [ -n "$USER_OUTPUT_PATH" ]; then
134
+ OUTPUT_DIR="$USER_OUTPUT_PATH"
135
+ elif [ -d "force-app/main/adk-eval-output" ]; then
136
+ OUTPUT_DIR="force-app/main/adk-eval-output"
137
+ fi
138
+
139
+ if [ -n "$OUTPUT_DIR" ]; then
140
+ cp /tmp/dx-org-comparison-${RUN_ID}.json "$OUTPUT_DIR/dx-org-analyze-result.json"
141
+ cp /tmp/dx-org-comparison-${RUN_ID}.md "$OUTPUT_DIR/dx-org-analyze-result.md"
142
+ fi
143
+ ```
144
+
145
+ ---
146
+
147
+ ## Introspect Workflow (Single-Org Mode)
148
+
149
+ Use this workflow when the user wants to inspect a single org's configuration, or when only one org is authenticated.
150
+
151
+ ### Introspect Step 1: Validate Connectivity
152
+
153
+ ```bash
154
+ sf data query --target-org <alias-or-username> --query "SELECT Id FROM Organization LIMIT 1" --json
155
+ ```
156
+
157
+ ### Introspect Step 2: Collect Data
158
+
159
+ ```bash
160
+ RUN_ID=$(date +%Y%m%d-%H%M%S)
161
+
162
+ python3 ./scripts/collect_org_data.py \
163
+ --org-alias "$ORG" \
164
+ --output /tmp/dx-org-analysis-${RUN_ID}
165
+ ```
166
+
167
+ ### Introspect Step 3: Generate Report
168
+
169
+ ```bash
170
+ python3 ./scripts/introspect_org.py \
171
+ --org /tmp/dx-org-analysis-${RUN_ID} \
172
+ --output /tmp/dx-org-analysis-${RUN_ID} \
173
+ --format both \
174
+ --label "OrgName"
175
+ ```
176
+
177
+ ### Introspect Step 4: Present Results
178
+
179
+ Read `/tmp/dx-org-analysis-${RUN_ID}.md` and present to the user. The report covers: metadata inventory, org settings, org limits, installed packages, licenses, system permissions, and deep data records. Then resolve the output directory and copy both files there:
180
+
181
+ ```bash
182
+ OUTPUT_DIR=""
183
+ if [ -n "$USER_OUTPUT_PATH" ]; then
184
+ OUTPUT_DIR="$USER_OUTPUT_PATH"
185
+ elif [ -d "force-app/main/adk-eval-output" ]; then
186
+ OUTPUT_DIR="force-app/main/adk-eval-output"
187
+ fi
188
+
189
+ if [ -n "$OUTPUT_DIR" ]; then
190
+ cp /tmp/dx-org-analysis-${RUN_ID}.json "$OUTPUT_DIR/dx-org-analyze-result.json"
191
+ cp /tmp/dx-org-analysis-${RUN_ID}.md "$OUTPUT_DIR/dx-org-analyze-result.md"
192
+ fi
193
+ ```
194
+
195
+ ---
196
+
197
+ ## Report Structure
198
+
199
+ The generated report includes:
200
+
201
+ 1. **Drift Score** — Weighted overall score (60% metadata, 30% permissions, 10% profiles) with severity level (LOW/MODERATE/HIGH/CRITICAL)
202
+ 2. **Summary Statistics** — Counts per metadata type with Identical/Different columns from deep data
203
+ 3. **Metadata Components by Type** — Only-in-Source, only-in-Target, shared per type
204
+ 4. **Profiles** — Shared, source-only, target-only
205
+ 5. **Installed Packages** — Version comparison, only-in-Source, only-in-Target
206
+ 6. **Package Components** — Namespaced components grouped by namespace
207
+ 7. **Org Permissions** — Boolean enabled/disabled diffs by category, plus value diffs
208
+ 8. **System Permissions** — PermissionsXxx fields on PermissionSet, per-set diffs
209
+ 9. **Org Values & Limits** — Grouped by Identity, Storage, API, Feature Limits
210
+ 10. **Licenses** — User, Permission Set, Package license quantity diffs
211
+ 11. **Deep Data** — Content-level diffs for Apex, Flows, Validation Rules, Custom Fields, etc.
212
+
213
+ ---
214
+
215
+ ## Rules / Constraints
216
+
217
+ | Constraint | Rationale |
218
+ |-----------|-----------|
219
+ | Always use `--json` with sf commands | Structured output for reliable parsing |
220
+ | Resolve orgs to usernames, not aliases | Aliases can be ambiguous; usernames are unique |
221
+ | Skip expired and disconnected orgs | Cannot query metadata from inaccessible orgs |
222
+ | Read-only — never deploy or modify | This skill compares only, never mutates either org |
223
+ | SF CLI auth only | All authentication through SF CLI credential store |
224
+ | Both orgs must be explicit | Never compare unnamed or implicit/default orgs |
225
+
226
+ ---
227
+
228
+ ## Troubleshooting
229
+
230
+ | Issue | Resolution |
231
+ |-------|------------|
232
+ | "No org found for \<alias\>" | Org not authenticated — run `sf org login web --alias <name>` |
233
+ | Fewer than 2 authenticated orgs | Authenticate additional orgs before comparing |
234
+ | "INVALID_SESSION_ID" or auth errors | Session expired — re-run `sf org login web --alias <name>` |
235
+ | Collection script exits with code 2 | Session expired — re-authenticate and retry |
236
+ | API limit errors during metadata listing | Use `--skip-deep-data` for a faster pass with less detail |
237
+ | Edition differences (DE vs EE) | Many differences are edition-inherent, not configuration drift |
238
+ | Large orgs timeout on deep data | Use `--skip-deep-data` flag; run full deep data on targeted follow-ups |
239
+
240
+ ---
241
+
242
+ ## Cross-Skill Integration
243
+
244
+ | Need | Delegate to |
245
+ |------|-------------|
246
+ | Retrieve specific metadata from an org | `platform-metadata-retrieve` |
247
+ | Deploy metadata to an org | `platform-metadata-deploy` |
248
+ | Create a scratch org for comparison | `dx-org-manage` |
249
+ | Switch default org after comparison | `dx-org-switch` |
250
+
251
+ ---
252
+
253
+ ## Reference File Index
254
+
255
+ | File | When to read |
256
+ |------|-------------|
257
+ | `references/collection-details.md` | For details on what the collection script gathers and how |
258
+ | `references/report-format.md` | For drift score formula and report section details |
259
+ | `scripts/collect_org_data.py` | Data collection script (per org) |
260
+ | `scripts/compute_diff.py` | Diff computation and report generation script |
261
+ | `scripts/introspect_org.py` | Single-org introspection report script |
@@ -0,0 +1,88 @@
1
+ # Collection Details
2
+
3
+ The `scripts/collect_org_data.py` script collects all data from a single org into a split directory with per-category JSON files.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ python3 ./scripts/collect_org_data.py \
9
+ --org-alias my-dev-org \
10
+ --output /tmp/org-data
11
+ ```
12
+
13
+ | Flag | Purpose |
14
+ |------|---------|
15
+ | `--org-alias` | SF CLI alias or username for the org (required) |
16
+ | `--output` | Output directory path (required) |
17
+ | `--skip-deep-data` | Skip Tooling API content-level queries (faster, less detail) |
18
+
19
+ ## Output Directory Structure
20
+
21
+ ```text
22
+ /tmp/org-data/
23
+ manifest.json # Lists category files
24
+ identity.json # Org ID, name, edition, errors
25
+ metadata.json # Metadata types and components
26
+ permissions.json # Org settings (OrganizationSettingsDetail)
27
+ org_values.json # Org limits
28
+ packages.json # Installed packages
29
+ licenses.json # User, PSL, and package licenses
30
+ system_permissions.json # PermissionsXxx on PermissionSet
31
+ deep_data.json # Apex bodies, Flow state, etc.
32
+ combined.json # All-in-one (backward compatibility)
33
+ ```
34
+
35
+ Split files enable parallel agent access — each agent loads only the section it needs.
36
+
37
+ ## What Gets Collected
38
+
39
+ ### Metadata (Name-Level)
40
+
41
+ All types discovered via `sf org list metadata-types` (typically 100+ types). Falls back to Tooling API queries if CLI metadata commands are unavailable. Namespaced (managed-package) components are tracked separately.
42
+
43
+ Metadata listing uses 12 parallel workers via `ThreadPoolExecutor`.
44
+
45
+ ### Deep Data (Content-Level via Tooling API)
46
+
47
+ | Category | Fields Collected |
48
+ |----------|-----------------|
49
+ | Apex Classes | Body hash (SHA-256), API version, LOC, status |
50
+ | Apex Triggers | Body hash comparison |
51
+ | Custom Fields | Per-object field sets, data type, length, precision, required |
52
+ | Validation Rules | Active/inactive state, error message |
53
+ | Flows | Process type, API version, active version ID |
54
+ | Named Credentials | Endpoint URL |
55
+ | Object Permissions | Per-profile CRUD (Create/Read/Edit/Delete) per object |
56
+ | Field Permissions | Per-profile Read/Edit per field (FLS) |
57
+ | Connected Apps | Presence |
58
+ | Custom Metadata Types | Presence |
59
+
60
+ Deep data queries use 8 parallel workers. SOQL pagination handles >2000 records automatically.
61
+
62
+ ### Org Settings
63
+
64
+ Queries `OrganizationSettingsDetail` via Tooling API (enabled/disabled features). Falls back to `SecurityHealthCheckRisks` if unavailable.
65
+
66
+ ### Org Limits
67
+
68
+ Uses `sf limits api display` (preferred) or REST `/limits/` endpoint (fallback). Captures max values for API calls, storage, features.
69
+
70
+ ### System Permissions
71
+
72
+ Dynamically discovers `PermissionsXxx` boolean fields via `sf sobject describe --sobject PermissionSet`, then queries all custom and profile-owned PermissionSet records. Filters out auto-generated permission sets (ID-based names).
73
+
74
+ ### Installed Packages
75
+
76
+ Queries `InstalledSubscriberPackage` via Tooling API. Captures name, namespace, and version.
77
+
78
+ ### Licenses
79
+
80
+ Queries `UserLicense`, `PermissionSetLicense`, and `PackageLicense` with total/used counts.
81
+
82
+ ## Exit Codes
83
+
84
+ | Code | Meaning |
85
+ |------|---------|
86
+ | `0` | Success |
87
+ | `1` | Fatal error (bad args, network failure, sf CLI not available) |
88
+ | `2` | Session expired (caller should re-authenticate and retry) |