@salesforce/afv-skills 1.65.1 → 1.66.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 (31) hide show
  1. package/package.json +1 -1
  2. package/skills/agentforce-offline-experimentation-run/SKILL.md +16 -15
  3. package/skills/agentforce-offline-experimentation-run/scripts/sfap_replay.py +3 -245
  4. package/skills/dx-devops-work-item-manage/SKILL.md +34 -17
  5. package/skills/dx-devops-work-item-manage/examples/common-workflows.md +49 -0
  6. package/skills/dx-devops-work-item-manage/references/commit-api.md +116 -0
  7. package/skills/experience-lwc-widget-assess/SKILL.md +79 -0
  8. package/skills/experience-lwc-widget-migrate/SKILL.md +50 -0
  9. package/skills/platform-role-hierarchy-apply/SKILL.md +177 -0
  10. package/skills/platform-widget-generate/SKILL.md +58 -41
  11. package/skills/platform-widget-generate/references/widget-bundle-validation.md +14 -0
  12. package/skills/service-agentforce-contact-center-coordinate/SKILL.md +1 -1
  13. package/skills/service-agentforce-contact-center-coordinate/references/number_management_api.md +11 -1
  14. package/skills/service-agentforce-contact-center-coordinate/scripts/fetch-numbers.sh +12 -6
  15. package/skills/service-agentforce-contact-center-coordinate/scripts/lib.sh +16 -0
  16. package/skills/service-agentforce-contact-center-coordinate/scripts/procure-number.sh +2 -1
  17. package/skills/service-concierge-portal-generate/references/portal-deploy-runbook.md +24 -11
  18. package/skills/service-digital-engagement-channel-configure/SKILL.md +25 -14
  19. package/skills/service-digital-engagement-channel-configure/references/onboarding_contract.md +143 -0
  20. package/skills/service-digital-engagement-deployment-configure/SKILL.md +61 -12
  21. package/skills/service-digital-engagement-deployment-configure/references/connect_api_creation.md +14 -5
  22. package/skills/service-digital-engagement-deployment-configure/references/onboarding_contract.md +146 -0
  23. package/skills/service-itsm-agentic-setup-incident-management/SKILL.md +23 -13
  24. package/skills/service-itsm-agentic-setup-incident-management/references/incident-persona-psg.md +7 -6
  25. package/skills/service-itsm-agentic-setup-incident-management/references/incident-preferences.md +9 -10
  26. package/skills/service-itsm-agentic-setup-incident-management/references/major-incident-management.md +6 -6
  27. package/skills/service-itsm-agentic-setup-incident-management/references/service-mgmt-privilege.md +1 -1
  28. package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +34 -25
  29. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +1 -1
  30. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +90 -86
  31. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/workflow.md +63 -38
@@ -0,0 +1,116 @@
1
+ # Commit through the DevOps Center API
2
+
3
+ Request, response and polling were verified live (dev org + Core hub, work item `WI-000001`, status In Progress). Error codes, no-access preflight behaviour and git-path sync behaviour come from source, not live tests. Use this when the user supplies a dev org alias and the changes are already deployed in that org.
4
+
5
+ ## Contents
6
+
7
+ - Orgs involved
8
+ - Preflight
9
+ - Request
10
+ - Response and errors
11
+ - Polling
12
+ - Confirming on the hub
13
+ - Differences from the git path
14
+ - Known limits
15
+
16
+ ## Orgs involved
17
+
18
+ | Org | Role | Used for |
19
+ |-----|------|----------|
20
+ | Dev org | Holds the metadata; where DX Inspector runs | `doceConnectionType`, `commitChanges`, `DevopsRequestInfo` polling |
21
+ | Hub org | Holds projects, work items, component lists | `sf devops work-item list`, status checks, confirming the commit |
22
+
23
+ The dev org reaches the hub server-side through the `ALMDevOpsCenterHub` named credential. The CLI cannot use that credential, so the hub needs its own `sf` login (`--target-org <hub-alias>`).
24
+
25
+ ## Preflight
26
+
27
+ ```bash
28
+ sf api request rest "/services/data/v67.0/connect/devops/doceConnectionType" --target-org <dev-org>
29
+ ```
30
+
31
+ The commit API is used at `v67.0`; the commands are not tied to the skill's `minApiVersion`. Treat a non-zero exit or an error body as failure for every `sf api request rest` call here (the no-access case was not tested live).
32
+
33
+ - 2xx with `{"isCore": true, "isManagedPackage": false}` — Core hub; proceed. This call itself goes through the hub named credential, so success is evidence the credential works.
34
+ - A failed call (no DX Inspector access, endpoint missing) — fall back to the git path.
35
+ - `isCore` not `true`, or `isManagedPackage: true` — this reference covers the Core path only; use the git path.
36
+ - Optional: `sf data query --target-org <dev-org> -q "SELECT DeveloperName FROM NamedCredential WHERE DeveloperName = 'ALMDevOpsCenterHub'"`.
37
+
38
+ ## Request
39
+
40
+ `POST /services/data/v67.0/connect/devops/commitChanges` on the **dev org**. The REST body is the input object itself. Wrapping it in `{"commitChanges": {...}}` fails with `JSON_PARSER_ERROR: Unrecognized field "commitChanges"`.
41
+
42
+ ```json
43
+ {
44
+ "isCommitCore": "true",
45
+ "workItemId": "<hub work item id>",
46
+ "projectId": "<hub project id>",
47
+ "workItemName": "WI-000001",
48
+ "workItemLink": "<url of the work item in the hub>",
49
+ "commitMessage": "<message>",
50
+ "changes": [
51
+ {"fullName": "MyClass", "type": "ApexClass", "operation": "modify"}
52
+ ]
53
+ }
54
+ ```
55
+
56
+ - `isCommitCore` is the **string** `"true"`. The Core path needs it together with a non-empty `workItemId`.
57
+ - `operation` is `add`, `modify` or `delete`.
58
+ - Rebuild the request body for **every** commit. Write it to a temp file (`mktemp`) and delete it afterwards, so a later `git add` cannot pick it up.
59
+ - Do not send `dataChanges` or `vcsRepo` (data deployment and the Managed Package path are out of scope).
60
+
61
+ ```bash
62
+ BODY=$(mktemp) # write the JSON above to "$BODY"; rm "$BODY" when done
63
+ sf api request rest "/services/data/v67.0/connect/devops/commitChanges" \
64
+ --method POST --body @"$BODY" --target-org <dev-org>
65
+ ```
66
+
67
+ Pass the file with the `@` prefix (`--body @<file>`); without it the CLI treats the path as the literal body.
68
+
69
+ ## Response and errors
70
+
71
+ Success: `{"reqId": "<uuid>", "status": "SUCCESS"}` — this means the request was accepted, not that the commit finished.
72
+
73
+ Request-level failures (e.g. `JSON_PARSER_ERROR` from a malformed body) come back as real HTTP errors: surface them and stop. Business failures are returned as HTTP 200 with `status: "ERROR"` and `reqId` holding `CODE:message`. Codes seen in source: `WORKITEM_NEVERED`, `VCS_NOT_AUTHENTICATED`, `COMMIT_API_FAILURE_CORE`, `COMMIT_API_FAILURE_CLIAAS`, `COMMIT_PAYLOAD_ERROR_*`, `COMMIT_DATA_VALIDATION_ERROR`, `COMMIT_FAILED`. Always check `status`, not the exit code alone.
74
+
75
+ Server-side preconditions: work item not closed (`WORKITEM_NEVERED`); status New, In Progress, In Review or Ready to Promote; the VCS connection is authenticated.
76
+
77
+ ## Polling
78
+
79
+ There is no status resource. Query the dev org:
80
+
81
+ ```bash
82
+ sf data query --target-org <dev-org> --json \
83
+ -q "SELECT Status, Message, ErrorDetails, RequestCompletionDate FROM DevopsRequestInfo WHERE RequestToken = '<reqId>'"
84
+ ```
85
+
86
+ - Terminal `Status` values: `SUCCESS` (not `COMPLETED`) and `ERROR`. A loop that waits for `COMPLETED` never exits.
87
+ - The commit observed took about 10 seconds. Poll every 10 seconds, deadline 12 minutes.
88
+ - `IN_PROGRESS` comes first; `RequestToken` equals the POST `reqId`.
89
+ - At the deadline the request is stuck (worker timeout is 10 minutes and the status can stay in progress). Report it and do not retry automatically; a retry is a new request that could race the stuck one.
90
+ - `Message`/`ErrorDetails` carry the failure text when `Status` is `ERROR`.
91
+
92
+ ## Confirming on the hub
93
+
94
+ ```bash
95
+ sf data query --target-org <hub-org> --json \
96
+ -q "SELECT Name, CommitReference, CreatedDate FROM WorkItemComponentList WHERE WorkItemId = '<id>' ORDER BY CreatedDate"
97
+ ```
98
+
99
+ A new row with a `CommitReference` hash confirms the commit was recorded. A new row with a null `CommitReference` (seen with unchanged components) means the request succeeded but nothing new was committed; report "nothing to commit", not a successful commit. An In Progress work item stays In Progress; a New one is expected to move to In Progress (not tested live).
100
+
101
+ ## Differences from the git path
102
+
103
+ | | Git path | API path |
104
+ |---|---|---|
105
+ | Committed content | Files on local disk | Components named in `changes`, retrieved from the dev org |
106
+ | Orgs | Hub only | Dev org for the commit, hub for lookup and confirmation |
107
+ | Author | The user's git identity | `DevOps Center <noreply@salesforce.com>` |
108
+ | Branch | Work item `branch` field | The work item name (`WI-000001`); the live test could not distinguish this from `branch` |
109
+ | Hub records | Created when the hub's external-commit sync picks the push up (only for In Progress, In Review, Ready to Promote) | Created immediately |
110
+ | Result tracking | git exit codes | `reqId` plus `DevopsRequestInfo` |
111
+
112
+ ## Known limits
113
+
114
+ - No rebase before push: a concurrent push to the same branch can make the commit fail as non-fast-forward.
115
+ - An empty change set can finish `SUCCESS` with nothing committed.
116
+ - A failed attempt can leave an orphaned component list on the hub.
@@ -0,0 +1,79 @@
1
+ ---
2
+ name: experience-lwc-widget-assess
3
+ description: "Assess whether a specific Lightning web component can serve its user's purpose as an HXL declarative widget. Use for LWC-to-widget feasibility, portability audits, and candidate screening; use experience-lwc-widget-migrate when the user wants the widget built."
4
+ metadata:
5
+ version: "1.0"
6
+ domains: ["Experience", "Platform", "Agentforce"]
7
+ minApiVersion: "68.0"
8
+ relatedSkills:
9
+ - "experience-lwc-widget-migrate"
10
+ - "platform-widget-generate"
11
+ ---
12
+
13
+ # Assess LWC to widget fit
14
+
15
+ Answer two questions for each LWC: **Can the current behavior be represented?** If not, **would a feasible widget still accomplish the user's goal?** Assess only; do not create metadata. A widget is its own declarative artifact, distinct from a Lightning Type. Its body renders supplied data with discovered tiles, bindings, formulas, and supported actions. It cannot run arbitrary LWC code, fetch after render, or retain reactive client state.
16
+
17
+ ## Evidence and decision
18
+
19
+ 1. Read the component's JS/TS, template, metadata, and relevant child components. State the user's goal in one sentence. Trace each visible value to its source and identify interactions essential to that goal. An `@api` property proves external input, **not** that an invocable action returns it. A user's explicit statement about action output is evidence for a feasibility assessment; verify the actual action contract and Lightning Type/renderer wiring before claiming a working migration.
20
+ 2. Map the **job** of each rendered element and action to the target surface. `platform-widget-generate` explains the discovery procedure; the actual `discoverUiComponents` response and `getUiComponentSchemas` results establish available blocks and attributes. Read its `references/widget-formulas.md` only when a transformation matters. Record the target channel from evidence rather than assuming a fixed channel or tile count. Without discovery, a documented pattern can support a **provisional candidate**, but absence from examples does not prove a block is unavailable. Use `review` when that uncertainty determines the verdict; never invent a tile, action, input submission mechanism, or formula.
21
+ 3. Check all dimensions, even after a failure: rendering vocabulary; retained state, imperative effects, and actions; data origin; and transformations. A bounded snapshot or pre-shaped collection can sometimes move into the invocable action's output. Inspect the action implementation before asserting that it can. A formula's scalar `MIN`/`MAX`, date, or regex support does not imply collection sorting or aggregation. Input forms require evidence that the current tiles can collect values **and** pass those values to an action; `sendMessage` alone does not prove that.
22
+ 4. Judge the current LWC and a proposed widget separately. A link or `sendMessage` may preserve an intent without preserving the original interaction, if the agent can actually perform the requested work with the required context. A carousel may become a product list; an order builder whose purpose is choosing quantities may not. Treat that change as a rebuild. For the widget candidate, consider a **specified proposed IA contract** when setting `rendersAsWidget`; state the contract change explicitly. If feasibility or purpose remains unresolved, use `review`, not a forced yes/no verdict.
23
+
24
+ Use these outcomes:
25
+
26
+ | Outcome | Meaning |
27
+ |---|---|
28
+ | `asIsVerdict: port-declarative` | Current visible behavior and data contract can be represented. |
29
+ | `asIsVerdict: needs-IA-contract-change` | Only a bounded output or pre-shaping change is needed. |
30
+ | `asIsVerdict: not-portable` | Some current behavior or visual has no verified equivalent. |
31
+ | `asIsVerdict: review` | Evidence cannot establish the current fit. |
32
+ | `asDisplayWidget.verdict: viable` | A verified or clearly identified candidate serves the goal, possibly after a stated contract change or rebuild. |
33
+ | `asDisplayWidget.verdict: pointless` | It can render but loses the user's goal. |
34
+ | `asDisplayWidget.verdict: blocked` | The proposed display/action cannot be represented. |
35
+ | `asDisplayWidget.verdict: review` | Capability, data provenance, or purpose is unresolved. |
36
+
37
+ List **every material blocker** in the report with its gate and evidence, citing a source location or concrete behavior. Gate names are `role`, `rendering vocabulary`, `behavior/interactivity`, `data source`, and `transformation`. Do not stop at the first failed dimension. `contractChange.shapeToAdd` names only fields or pre-shaped values the action could plausibly return; never invent a payload. A viable adaptation may need a contract change even when the current LWC is `not-portable`. If a proposed widget changes the UX, set `isRebuild: true` and name the lost interaction in the report. Mark uncertain conclusions low confidence and explain what would resolve them.
38
+
39
+ ## Output
40
+
41
+ Produce two artifacts. When an output location is given, write them as `verdict.json` and `report.md`.
42
+
43
+ **Verdict** — the machine-readable decision that `experience-lwc-widget-migrate` gates on. One JSON object per component; for a folder, an array. Keep exactly these fields:
44
+
45
+ ```json
46
+ {
47
+ "component": "caseDetailsPanel",
48
+ "role": "renderer",
49
+ "asIsVerdict": "port-declarative",
50
+ "contractChange": {"suggested": false, "shapeToAdd": []},
51
+ "asDisplayWidget": {"verdict": "viable", "rendersAsWidget": true, "stillServesPurpose": true, "isRebuild": false},
52
+ "needsHumanReview": false
53
+ }
54
+ ```
55
+
56
+ `role` is `renderer`, `input`, or `interactive`. `stillServesPurpose` is boolean or `"unknown"`; `rendersAsWidget` is boolean or `null` when capability is unresolved. For `viable`, both are true. For `pointless`, rendering is true and purpose false. For `blocked`, rendering is false and purpose is `"unknown"`. For `review`, keep unresolved fields unknown/null. Set `needsHumanReview: true` for either `review` verdict or unknown purpose. Suggest a contract change only when a value the widget displays is **not** already in the action's output (for example, it is fetched after render). Data already in the payload needs no contract change, even when the LWC is `not-portable` or the widget is a rebuild. Any suggested contract change needs a nonempty `shapeToAdd`; `needs-IA-contract-change` requires such a suggestion. `port-declarative` has no contract change.
57
+
58
+ **Report** — the human-review document. For a folder, one section per component. Use this structure:
59
+
60
+ ```markdown
61
+ # <component>: <one-line conclusion>
62
+
63
+ **Verdict:** <asIsVerdict> → <asDisplayWidget.verdict> (rebuild or not) · **Confidence:** high | low · **Needs human review:** yes | no
64
+
65
+ ## Why
66
+ The user's goal, what maps cleanly, and what decides the verdict.
67
+
68
+ ## Blockers
69
+ Each material blocker: gate, signal, evidence. "None" for `port-declarative`.
70
+
71
+ ## Suggested contract change
72
+ Each field to add and why it must move into the action output. Omit when none is suggested.
73
+
74
+ ## What the widget keeps / drops
75
+ Kept actions and dropped or replaced interactions.
76
+
77
+ ## Open questions
78
+ What would resolve any low-confidence conclusion.
79
+ ```
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: experience-lwc-widget-migrate
3
+ description: "Convert a specific existing Lightning web component (LWC) into a declarative HXL / Agentforce widget, end to end. Gates on experience-lwc-widget-assess and stops when a widget cannot serve the LWC's purpose; otherwise hands the derived field shape to platform-widget-generate and wires the widget as the Lightning Type renderer. TRIGGER when: the user wants to convert, migrate, port, rebuild, or turn a named LWC into a widget or declarative Agentforce widget, or asks for a widget built from an existing LWC component. DO NOT TRIGGER when: the user only wants to know whether an LWC can become a widget (use experience-lwc-widget-assess), or wants a widget for a data shape, entity, or Lightning Type with no existing LWC (use platform-widget-generate or platform-lightning-type-widget-coordinate)."
4
+ metadata:
5
+ version: "1.0"
6
+ domains: ["Experience", "Platform", "Agentforce"]
7
+ minApiVersion: "68.0"
8
+ relatedSkills:
9
+ - "experience-lwc-widget-assess"
10
+ - "platform-widget-generate"
11
+ - "platform-custom-lightning-type-generate"
12
+ - "platform-lightning-type-widget-coordinate"
13
+ mcpTools:
14
+ metadata-experts:
15
+ tools: ["execute_metadata_action"]
16
+ semver: ">=1.0.0"
17
+ ---
18
+
19
+ # Migrate an LWC to a widget
20
+
21
+ Use the LWC to recover the **user's goal and data contract**, then build the closest useful widget. A widget is its own declarative artifact. `platform-widget-generate` authors it; a Lightning Type renderer may need separate wiring to make it reachable. This skill owns the comparison, handoff, integration check, and honest final status.
22
+
23
+ ## 1. Decide whether to proceed
24
+
25
+ Read and apply `experience-lwc-widget-assess` to the component, its action output, and any existing Lightning Type/renderer. Preserve its verdict and the key findings from its report (blockers, contract change, lost interactions) in your report; do not write the assessment's own `verdict.json` or `report.md` files. Stop on `pointless`, `blocked`, `review`, or `needsHumanReview: true`. Proceed on `viable` only when the intended change is explicit:
26
+
27
+ - For `port-declarative`, preserve the current visible fields and actions.
28
+ - For `needs-IA-contract-change`, identify the exact bounded output fields or pre-shaped values required. A widget against a **proposed** contract is a draft until the action returns that contract; do not call it working.
29
+ - For `not-portable` with a viable adaptation, describe the replacement UX and each lost interaction. Call it a rebuild, not a faithful port.
30
+
31
+ ## 2. Derive the generator handoff
32
+
33
+ Trace each value the proposed widget displays to the actual or proposed action output. Before authoring, verify the action contract rather than relying only on an assessment prompt's description. Pass `platform-widget-generate` an ordered field shape (`name`, `type`, `required`), nested/list item fields, the intended tile role of each visible element, and supported terminal actions. Preserve render order and field paths. Use its discovery results for tile definitions and attributes, its documented formula catalog for expressions, and its instructions for schema types and bundle layout; do not maintain a second catalog here. A formula can cover supported scalar/date/regex operations, while collection restructuring may require pre-shaped output.
34
+
35
+ If an LWC fixture or action example contains sample data, it can help infer the shape; sample data is optional and is **not** a documented generator input or a substitute for the action contract. Do not invent sample fields. For an action such as `sendMessage`, verify that the required context reaches the agent and that the agent has a way to perform the requested work. Do not assume a widget input form can submit collected values without a discovered, validated mechanism.
36
+
37
+ ## 3. Generate and connect
38
+
39
+ Load `platform-widget-generate` and follow its mandatory live `discoverUiComponents` and `getUiComponentSchemas` calls. If the `metadata-experts` MCP or discovery is unavailable, stop before authoring a bundle; report the completed assessment and proposed field shape. The generator owns its three widget files and self-validation. Do not copy its UEM syntax or validation checklist into this skill.
40
+
41
+ For an Agentforce renderer migration, also establish the Lightning Type that receives the action output. A bundle alone is not connected to the action:
42
+
43
+ - **Existing compatible Lightning Type:** inspect its schema and current `renderer.json`. After generation, read `platform-custom-lightning-type-generate/references/widget-rendition.md`, wire the widget with field mappings, and follow `platform-lightning-type-widget-coordinate`'s renderer checks. If the existing renderer points to another widget or LWC, surface the replacement/merge decision before changing it.
44
+ - **No compatible Lightning Type:** resolve the action's output class and a Lightning Type name from the project or the user's request. For the coordinator's supported Apex-backed route, use `platform-lightning-type-widget-coordinate` with that type and the assessed output shape; let it own bundle generation and renderer wiring. That coordinator does not cover every type. If the output/type route cannot be established, stop and identify the missing integration step. Do not claim a complete migration.
45
+
46
+ If the user requested a **bundle only**, report it explicitly as an unconnected bundle. Otherwise, completion requires both the bundle and renderer wiring.
47
+
48
+ ## 4. Check the result
49
+
50
+ Use the generator's bundle self-validation. Then compare the result to the assessed candidate: every retained visible field has a binding to the real/proposed payload; list item paths and supported actions match; omitted behavior and contract changes are stated; and, for a renderer migration, the Lightning Type points to this widget with matching attributes. Static checks establish structure and mapping, not deployed runtime behavior. Report assessment, created paths, validation results, and one of: `migrated and wired`, `bundle only`, `draft pending IA contract`, or `stopped`, with the reason.
@@ -0,0 +1,177 @@
1
+ ---
2
+ name: platform-role-hierarchy-apply
3
+ description: "Bulk-realign an existing Salesforce UserRole hierarchy from an external org-chart artifact (PDF, xlsx, or plain-text list of role-and-parent pairs) in one admin-approved manifest. Clarify scope and whether users move with roles, resolve identities that do not match verbatim, diff the artifact against the current tree, and submit create, rename, reparent, empty-role delete, and user-assignment mutations together. Triggers on \"reorganize the role hierarchy\", \"apply this org chart to roles\", \"bulk update role parents\", \"realign roles from PDF\", \"restructure the role hierarchy\", \"reparent roles from this spreadsheet\". Do not trigger for a single role create, rename, or reparent with no other changes, or for asking which roles exist or how they are nested — that is `managing-roles`. Do not trigger for territory, account, or forecast hierarchies, or for authoring sharing rules."
4
+ metadata:
5
+ version: "1.0"
6
+ ---
7
+
8
+ # Role Hierarchy Restructure
9
+
10
+ Apply many UserRole changes from an artifact in one admin-approved manifest.
11
+
12
+ If the admin only asks which roles exist or how they are nested, hand off to `managing-roles`. Do not query, do not ask the step 0 questions, and do not submit.
13
+
14
+ ## Inputs
15
+
16
+ - PDF org chart (text-selectable), `.xlsx` / `.csv` with `role_name` and `parent_role_name` (empty parent = top-level), or plain text with one `Role: Parent` pair per line.
17
+ - Or a pre-parsed list of `{roleName, parentRoleName}` objects.
18
+ - Optional mapping hints when a chart name is not the org's `Name` or `DeveloperName`.
19
+
20
+ ## Flow
21
+
22
+ ### 0. Clarify before querying
23
+
24
+ Ask anything still unresolved. Do not guess.
25
+
26
+ - **Scope.** Default is selected branches: roles absent from the artifact stay. "Full hierarchy" still does not delete those roles unless the admin asked for each deletion.
27
+ - **User reassignment.** Default is no. Do not emit `AssignUser` or `MoveUser` without an explicit yes.
28
+ - **Ambiguous identities.** Resolve every role and user in one batch. See step 2.
29
+ - **Conflicts.** Two parents for one role, or a role parented to itself: stop until the admin picks.
30
+
31
+ If the artifact matches fewer than 20% of its roles to the current tree, stop and ask whether it is a greenfield replacement, the wrong file, or a mapping problem. Do not build a manifest until the admin confirms a greenfield replacement. Between 20% and 50%, show the fraction in the preview and continue.
32
+
33
+ ### 1. Read the current hierarchy
34
+
35
+ ```text
36
+ SELECT Id, DeveloperName, Name, ParentRoleId
37
+ FROM UserRole
38
+ LIMIT 10000
39
+ ```
40
+
41
+ Match artifact roles by `DeveloperName`, then case-insensitive `Name`.
42
+
43
+ ### 2. Parse the artifact and resolve identities
44
+
45
+ Normalize to `{roleName, parentRoleName, users?}` and show that list to the admin before the diff. A PDF that is not text-selectable cannot be parsed. Ask for a plain-text `Role: Parent` list.
46
+
47
+ Roles: exact `DeveloperName`, then case-insensitive `Name`. No fuzzy match. When more than one role matches, list each candidate's `DeveloperName` and `Id` and wait. Do not pick one. Unresolved names go back to step 0.
48
+
49
+ Users, only after reassignment was confirmed:
50
+
51
+ 1. 18-character id starting with `005` → `User.Id`
52
+ 2. Email → `User.Email`
53
+ 3. Username → `User.Username`
54
+ 4. Display name → `User.Name`, and only when exactly one active user matches
55
+
56
+ `IsActive = false` is a non-match. A user listed under two roles is a conflict. Ask which role that user should land under, or whether to drop the reassignment. Do not submit until the admin answers.
57
+
58
+ Stop on a cycle in the target graph. Report the path, for example `A → B → C → A`, and ask for a corrected artifact. Do not submit.
59
+
60
+ ### 3. Build the manifest
61
+
62
+ Each mutation has `operationId`, `operationType`, `dependsOnOperationIds`, and one nested payload. `dependsOnOperationIds` is the order that matters.
63
+
64
+ | operationType | Nested object | Use when |
65
+ |---|---|---|
66
+ | `CreateRole` | `createRole` | Role is in the artifact and not in the org. Set `roleReference`, `developerName`, `name`, and `parentRoleId` or `parentRoleReference`. Default the three account-owner access fields to the org defaults when the artifact omits them. |
67
+ | `ReparentRole` | `reparentRole` | Role exists and the parent differs. Set `targetRoleId` or `targetRoleReference`, `expectedParentRoleId`, and `desiredParentRoleId` or `desiredParentRoleReference`. |
68
+ | `RenameRole` | `renameRole` | Same role, different label or developer name. Set the expected and desired name fields. |
69
+ | `DeleteRole` | `deleteRole` | Admin asked to remove an empty role. Set `targetRoleId`, `expectedParentRoleId`, `expectedAssignedUserIds`, and `expectedDirectChildRoleIds`. |
70
+ | `AssignUser` or `MoveUser` | `userRoleAssignment` | Reassignment was confirmed. Set `userId`, `expectedCurrentRoleId`, and `desiredRoleId` or `desiredRoleReference`. |
71
+
72
+ `DeleteRole` applies only when the role has no direct children and no assigned users at the moment it runs. Reparent those children and move those users in earlier mutations, and put those `operationId`s in `dependsOnOperationIds`. The delete's expected user and child lists are then empty. If the admin declined reassignment, do not delete a role that still has users.
73
+
74
+ A mutation that names a role neither in the org nor created in this manifest is `missing-parent`. Name the roles that point at it. Ask whether to add a `CreateRole` for it in this manifest, skip those rows, or abort. Do not submit with that parent missing.
75
+
76
+ A cycle in `dependsOnOperationIds` blocks the submit. Give every new role a `roleReference` string and point parents and assignments at that string.
77
+
78
+ ### 4. Validate, then preview
79
+
80
+ Block the submit when any of these remain: a cycle, a missing parent, a duplicate `developerName`, a dependency cycle, an `AssignUser` or `MoveUser` without the step 0 yes, a `DeleteRole` whose expected user or child lists are not empty, or overlap under 20% without a confirmed greenfield replacement.
81
+
82
+ On a duplicate `developerName`, show both rows and wait until the admin picks one or renames one.
83
+
84
+ Warn in the preview, and continue only after an explicit yes:
85
+
86
+ - A changed role is referenced by `Territory2.UserRoleId` or a forecast hierarchy override.
87
+ - A changed role has `PortalType != 'None'`.
88
+ - A changed role has more than 500 users (`SELECT COUNT() FROM User WHERE UserRoleId = :id`).
89
+
90
+ ```text
91
+ Restructure preview
92
+ -------------------
93
+ ReparentRole : 42
94
+ CreateRole : 3
95
+ RenameRole : 1
96
+ DeleteRole : 2
97
+ MoveUser : 2
98
+
99
+ Territory / forecast references : 1
100
+ Portal roles : 0
101
+ Roles with more than 500 users : 2
102
+
103
+ Approve this restructure? [yes / no]
104
+ ```
105
+
106
+ `yes` submits. Anything else does not. Roles left in the org and absent from the artifact stay in place.
107
+
108
+ ### 5. Submit
109
+
110
+ ```text
111
+ POST /services/data/v{version}/sharing/role-hierarchy-restructure
112
+ {
113
+ "operation": "Save",
114
+ "mutations": [
115
+ {
116
+ "operationId": "reparent-sales-vp",
117
+ "operationType": "ReparentRole",
118
+ "dependsOnOperationIds": [],
119
+ "reparentRole": {
120
+ "targetRoleId": "00E000000000001",
121
+ "expectedParentRoleId": "00E000000000002",
122
+ "desiredParentRoleReference": "cro"
123
+ }
124
+ },
125
+ {
126
+ "operationId": "delete-legacy-region",
127
+ "operationType": "DeleteRole",
128
+ "dependsOnOperationIds": ["reparent-legacy-children"],
129
+ "deleteRole": {
130
+ "targetRoleId": "00E000000000003",
131
+ "expectedParentRoleId": "00E000000000002",
132
+ "expectedAssignedUserIds": [],
133
+ "expectedDirectChildRoleIds": []
134
+ }
135
+ }
136
+ ]
137
+ }
138
+ ```
139
+
140
+ `CreateRole`, `RenameRole`, `AssignUser`, and `MoveUser` use the same envelope with their nested object from step 3.
141
+
142
+ Send the approved manifest once. Do not edit `UserRole` records one by one.
143
+
144
+ ### 6. Read the response
145
+
146
+ - `Applied` — structural and sharing changes committed.
147
+ - `AppliedSharingPending` — hierarchy is live; sharing catches up on its own. Do not poll.
148
+ - `AsyncExecutionRequired` — the server queued the manifest. Do not poll.
149
+ - `RoleOperationInProgress` — another restructure is running. Stop. Do not queue the manifest. Ask the admin to retry once the other operation finishes.
150
+ - `NotApplied` — nothing committed. Show `reasonCodes` and stop.
151
+ - `404` on submit — API not enabled for this client tier. Halt, tell the admin, do not retry, do not fall back to per-record `/sobjects/UserRole` edits.
152
+
153
+ For each `operationResults[]` entry, `outcome` is `Applied`, `NotApplied`, or `DependencyNotApplied`. Show `reasonCode` for any entry that is not `Applied`. Do not retry and do not roll back.
154
+
155
+ ## Out of scope
156
+
157
+ - A single role create, rename, or reparent with no other changes, and any request that only asks which roles exist or how they are nested (`managing-roles`).
158
+ - Territory, account, and forecast hierarchies.
159
+ - Authoring sharing rules.
160
+ - Image-only PDFs. Ask for text.
161
+ - Rollback. Report what the response says was committed.
162
+ ---
163
+
164
+ <!-- skill-validate: ignore-start -->
165
+ <!-- codey:operations start -->
166
+ ## Operations Reference
167
+
168
+ Operations this skill uses — routing and dependency reference.
169
+
170
+ | Operation | Purpose | Status | Call | Depends on |
171
+ |-----------|---------|--------|------|------------|
172
+ | `read-role-hierarchy` | read | — | `GET /services/data/v68.0/query` | — |
173
+ | `count-users-per-role` | read | — | `GET /services/data/v68.0/query` | `read-role-hierarchy` |
174
+ | `submit-role-hierarchy-restructure` | write | — | `POST /services/data/v68.0/sharing/role-hierarchy-restructure` | `read-role-hierarchy` |
175
+ | `verify-role-hierarchy-restructure` | verify | — | `GET /services/data/v68.0/query` | `submit-role-hierarchy-restructure` |
176
+ <!-- codey:operations end -->
177
+ <!-- skill-validate: ignore-end -->
@@ -2,17 +2,16 @@
2
2
  name: platform-widget-generate
3
3
  description: "Use this skill to author a complete HXL WidgetBundle (UEM body + schema.json + -meta.xml). TRIGGER when: user asks for a widget, mosaic, fragment, card, or rich UI surface for any subject, domain, feature, or entity noun; the prompt names only an entity or data shape without invoking Lightning Types, CLTs, or Apex-backed types. DO NOT TRIGGER when: the prompt explicitly says 'Lightning Type', 'CLT', 'Custom Lightning Type', 'Apex-backed type', or references '@apexClassType/...' (use platform-lightning-type-widget-coordinate); authoring a custom-LWC renderer for a Custom Lightning Type (use platform-custom-lightning-type-generate); or editing only an LWC component."
4
4
  metadata:
5
- version: "1.4"
5
+ version: "1.5"
6
6
  domains: ["Platform", "Agentforce"]
7
7
  minApiVersion: "68.0"
8
+ cliTools:
9
+ - tool: ["sf"]
10
+ semver: ">=2.0.0"
8
11
  relatedSkills:
9
12
  - "platform-apex-generate"
10
13
  - "platform-custom-lightning-type-generate"
11
14
  - "platform-lightning-type-widget-coordinate"
12
- mcpTools:
13
- metadata-experts:
14
- tools: ["execute_metadata_action"]
15
- semver: ">=1.0.0"
16
15
  ---
17
16
 
18
17
  # Generating a Widget Bundle
@@ -67,23 +66,49 @@ The first child of `tile/widget.children` SHOULD be a single `tile/column`. All
67
66
 
68
67
  ---
69
68
 
70
- ## Available Metadata Actions
69
+ ## Available Invocable Actions
70
+
71
+ Both are standard invocable actions on the target org.
72
+
73
+ ### Calling Convention
74
+
75
+ Fire each action with whichever mechanism is available: the H360 MCP `dispatch` tool (a `POST` to the endpoint path with the request body) or `sf api request rest`. Use `v{version}` = this skill's `minApiVersion`. Never use `curl`, and never extract the org's access token.
76
+
77
+ ```bash
78
+ sf api request rest '/services/data/v{version}/actions/standard/<actionName>' --method POST --body '<request body>' --target-org <org>
79
+ ```
80
+
81
+ ### Response Handling
82
+
83
+ Each response is an array with one envelope (`dispatch` nests it under `body`). Check three failure layers, in order:
84
+
85
+ | Layer | Signal | Action |
86
+ |---|---|---|
87
+ | Transport / auth | `dispatch` `status_code` is not 200, or CLI exits non-zero | Surface the error |
88
+ | Action | `[0].isSuccess` is `false`, or `[0].errors` is non-empty | Surface `errors[].message` |
89
+ | Per-entry (`getUiComponentSchemas` only) | any `componentSchemas[].success` is `false`, even when `[0].isSuccess` is `true` | Treat that block as unavailable |
90
+
91
+ On success, read `[0].outputValues`.
71
92
 
72
93
  ### discoverUiComponents
73
94
 
74
95
  **Purpose:** Discover the palette of blocks available for composition.
75
96
 
76
- **Required parameters:** `actionName: "discoverUiComponents"`, `metadataType: "FRAGMENT"`, `parameters.pageType: "FRAGMENT"`. Optional: `searchQuery` to filter by name/description.
97
+ **Endpoint:** `POST /services/data/v{version}/actions/standard/discoverUiComponents`
98
+
99
+ **Request body:** `{"inputs":[{"pageType":"FRAGMENT"}]}`
77
100
 
78
- **Returns:** list of `{ definition, description, label, attributes? }`.
101
+ **Returns:** `outputValues.componentDefinitions[]` — list of `{ definition, description, label }`.
79
102
 
80
103
  ### getUiComponentSchemas
81
104
 
82
- **Purpose:** Fetch JSON schemas (property types, required vs optional, validation) for selected blocks.
105
+ **Purpose:** Fetch schemas (property types, required vs optional, validation) for selected blocks.
83
106
 
84
- **Required parameters:** `actionName: "getUiComponentSchemas"`, `metadataType: "FRAGMENT"`, `parameters.pageType: "FRAGMENT"`, `parameters.componentDefinitions: ["namespace/definition", ...]`. Optional: `includeKnowledge` (default `true`).
107
+ **Endpoint:** `POST /services/data/v{version}/actions/standard/getUiComponentSchemas`
85
108
 
86
- **Returns:** `componentSchemas[]` — success entries carry the JSON schema, failure entries carry an error message. Partial failures are supported.
109
+ **Request body:** `{"inputs":[{"pageType":"FRAGMENT","componentDefinitions":["namespace/definition", ...]}]}`
110
+
111
+ **Returns:** `outputValues.componentSchemas[]` — list of `{ definition, success, schema }` (`schema` is a YAML string). Partial failures are supported.
87
112
 
88
113
  > Never pass `tile/widget` to `getUiComponentSchemas` — it is a fixed wrapper, not a queryable component.
89
114
 
@@ -169,11 +194,11 @@ Widgets express *intent*, not pixels. Each surface provides a default look and f
169
194
  - If `lightningTypeSchema` was passed by the orchestrator → derive per `references/schema-from-lightning-type.md`.
170
195
  - Otherwise → infer the list directly from the user prompt (pasted JSON payload, enumerated field list, or descriptive prose).
171
196
 
172
- 2. **Discover blocks (REQUIRED — do NOT skip).** Call the `discoverUiComponents` metadata action via `execute_metadata_action`. Use property types from the widget spec to seed `searchQuery` (text → `"text"`, number → `"number"`). **If `discoverUiComponents` returns `success: false`, an error, or an empty list, STOP and surface the error verbatim — do not improvise block names from memory, prior runs, or training data. Re-run discover with a different `searchQuery` only if the failure is search-query-specific.**
197
+ 2. **Discover blocks (REQUIRED — do NOT skip).** Call the `discoverUiComponents` invocable action (see *Available Invocable Actions*) and match widget-spec property types against the returned `label`/`description` values. **If `isSuccess` is `false`, `errors` is non-empty, or `componentDefinitions` is empty, STOP and surface the error verbatim — do not improvise block names from memory, prior runs, or training data.**
173
198
 
174
199
  3. **Select blocks.** Choose one block per widget-spec property, plus structural primitives from *Layout Best Practices*.
175
200
 
176
- 4. **Get block schemas (REQUIRED — do NOT skip).** Call the `getUiComponentSchemas` metadata action via `execute_metadata_action` for the selected blocks. Review property metadata. **If `componentSchemas` returns all-failure or empty, STOP and surface the error — do not improvise from existing widgets in the project.**
201
+ 4. **Get block schemas (REQUIRED — do NOT skip).** Call the `getUiComponentSchemas` invocable action for the selected blocks. Review property metadata. **If `isSuccess` is `false`, `componentSchemas` is empty, or any entry has `success: false`, do not use that block — re-select a different one from step 2's results; if none works, STOP and surface the error. Never improvise from existing widgets in the project.**
177
202
 
178
203
  5. **Build the UEM tree (example reads REQUIRED — do NOT skip).** First, identify which patterns match the widget spec and read each matching example file from this skill's own `examples/` directory (`<skill-root>/examples/`):
179
204
 
@@ -236,17 +261,8 @@ Widgets express *intent*, not pixels. Each surface provides a default look and f
236
261
 
237
262
  Write all three before proceeding to self-validation.
238
263
 
239
- 9. **Self-validate.** Before reporting, confirm each check below and report each result individually (`pass` or `fail (<reason>)`). Do **not** summarize as a single "all passed" line — list every check so a reviewer can spot a silent skip.
240
- - **`schema-parses`** — `<pkgDir>/uiWidgets/<widgetName>/schema.json` parses as JSON.
241
- - **`schema-root-keys`** — root has `title` (string), `type: "object"`, and `properties.attributes` (object) — where `properties.attributes` carries `lightning:type: "lightning__objectType"` and a nested `properties` map. No `unevaluatedProperties: false`.
242
- - **`schema-leaf-types`** — every leaf under `properties.attributes.properties` carries a `lightning:type`. Singular nested inner-class fields carry `lightning:type` set to the inner Apex class reference (`@apexClassType/<namespace>__<OuterClass>$<InnerClass>`); the nested shape is not redeclared. `List<InnerClass>` fields carry `lightning:type: "lightning__listType"` with `items.lightning:type` set to the inner Apex class reference (`@apexClassType/<namespace>__<OuterClass>$<InnerClass>`), not a redeclared field map — see `references/schema-from-lightning-type.md`. **When the list has no Apex-backed type** (schema inferred from the prompt), `items.lightning:type: "lightning__objectType"` MUST carry an inline nested `properties` map for every item field the body binds via `{!$item.X}`.
243
- - **`bindings-resolve`** — every `{!$attrs.X}` (or `{!$attrs.<outerField>.<innerField>}` for nested objects) in `<widgetName>.json` resolves to a property under `schema.json` `properties.attributes.properties`, and every `{!$item.X}` resolves to a `forItem` loop variable defined upstream. This applies to `$attrs`/`$item` references **inside formula expressions** too.
244
- - **`formulas-supported`** — every function name used inside a `{!...}` expression appears in the *Supported functions* list in `references/widget-formulas.md`.
245
- - **`body-envelope`** — `<widgetName>.json` root has `type: "lightning__agentforceWidget"` and a `contentBody` object whose `widgetBody` carries the UEM tree rooted at `tile/widget`. No node in the tree — root or non-root — carries a `type` key.
246
- - **`metaxml-wellformed`** — `<widgetName>.uiwidget-meta.xml` parses as well-formed XML.
247
- - **`metaxml-elements`** — `<widgetName>.uiwidget-meta.xml` has root `<UiWidgetBundle>` and contains `<masterLabel>` (non-empty), `<description>` (non-empty), and `<widgetType>JSON</widgetType>`.
248
- - **`files-present`** — all three files exist at the resolved `<pkgDir>/uiWidgets/<widgetName>/` path.
249
- - **`button-actions-present`** — every `tile/button` in `<widgetName>.json` has an `actions.click` action node whose `definition` is `action/openLink` or `action/sendMessage`.
264
+ 9. **Self-validate.** Before reporting, run the checks and report each result individually (`pass` or `fail (<reason>)`). Do **not** summarize as a single "all passed" line — list every check so a reviewer can spot a silent skip.
265
+ Run every check in `references/widget-bundle-validation.md`.
250
266
 
251
267
  ---
252
268
 
@@ -256,7 +272,7 @@ Widgets express *intent*, not pixels. Each surface provides a default look and f
256
272
  |---|---|
257
273
  | Block definitions follow `{namespace}/{blockName}` and must match `discoverUiComponents` output | Runtime resolves blocks by exact definition string |
258
274
  | Never pass `tile/widget` to `getUiComponentSchemas` | It is a fixed wrapper, not a queryable component |
259
- | Always supply `parameters` (with required keys) when calling `execute_metadata_action` | Missing parameters cause hard failure, not partial result |
275
+ | Read `references/` and `examples/` files only at the step that names them | Upfront reads waste context and precede the discovery result that determines which files are needed |
260
276
  | Every `{!$attrs.X}` in the body resolves to a property in the widget `schema.json` | No invented fields |
261
277
  | Every `tile/button` carries an `actions.click` entry using `action/openLink` or `action/sendMessage` only | These are the only two supported tile action definitions; an action-less button renders disabled |
262
278
  | No `$(…)`, backticks, `<(…)`, brace expansion `{a,b,c}`, or `eval`/`exec` in any Bash tool call | Vibes' safe-shell filter forces manual approval on these patterns even in Bypass mode. Emit separate commands (`mkdir -p a && mkdir -p b`) or print each value with its own command and reason about the output — do not capture into a shell variable |
@@ -265,26 +281,27 @@ Widgets express *intent*, not pixels. Each surface provides a default look and f
265
281
 
266
282
  ## Gotchas
267
283
 
268
- | Issue | Resolution |
269
- |---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
270
- | `getUiComponentSchemas` returns a partial-failure entry | Pick a different block from `discoverUiComponents`; do not silently continue without a schema |
271
- | Body references `{!$attrs.foo}` but `foo` is not under `schema.json` `properties.attributes.properties` | Add `foo` to `schema.json` `properties.attributes.properties` OR remove the body reference |
284
+ | Issue | Resolution|
285
+ |---|---|
286
+ | `getUiComponentSchemas` returns a partial-failure entry | Pick a different block from `discoverUiComponents`; do not silently continue without a schema|
287
+ | Body references `{!$attrs.foo}` but `foo` is not under `schema.json` `properties.attributes.properties` | Add `foo` to `schema.json` `properties.attributes.properties` OR remove the body reference|
272
288
  | Output written outside `<pkgDir>/uiWidgets/<widgetName>/` | `<pkgDir>` = `<packageDirectories[].path>/main/default` (see `references/widget-bundle-layout.md`). Dropping the `main/default/` segment is the common cause of widgets landing at `force-app/uiWidgets/...` instead of `force-app/main/default/uiWidgets/...` |
273
- | `if` bound directly to a raw string or number relies on truthiness and is unreliable | Bind to a `lightning__booleanType` property, or use a formula comparison that evaluates to boolean |
274
- | `tile/button` renders but does nothing when clicked | No `actions.click` entry was set — an action-less button renders disabled by design. Add one (see *Actions* above) |
275
- | Using `action/sendMessage` for pure navigation, or `action/openLink` when the agent should respond | `action/openLink` is synchronous and does not consume a turn; `action/sendMessage` is asynchronous and earns a fresh turn. Pick the one matching the intended UX |
289
+ | `if` bound directly to a raw string or number relies on truthiness and is unreliable | Bind to a `lightning__booleanType` property, or use a formula comparison that evaluates to boolean|
290
+ | `tile/button` renders but does nothing when clicked | No `actions.click` entry was set — an action-less button renders disabled by design. Add one (see *Actions* above)|
291
+ | Using `action/sendMessage` for pure navigation, or `action/openLink` when the agent should respond | `action/openLink` is synchronous and does not consume a turn; `action/sendMessage` is asynchronous and earns a fresh turn. Pick the one matching the intended UX|
276
292
 
277
293
  ---
278
294
 
279
295
  ## Reference File Index
280
296
 
281
- | File | When to read |
282
- |---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
283
- | `references/widget-meta-directives.md` | For `forEach` / `forItem` (iteration) and `if` (conditional rendering), including nested loops |
284
- | `references/schema-from-lightning-type.md` | When `lightningTypeSchema` is provided; how to derive the widget `schema.json` from an Apex-backed Lightning Type |
285
- | `references/widget-bundle-layout.md` | Folder layout, `-meta.xml` shape, `<pkgDir>` resolution rules |
286
- | `references/widget-formulas.md` | Supported formula functions and operators |
287
- | `examples/single-object.json` | Single-object pattern (root binding via `{!$attrs.X}`, no iteration) |
297
+ | File | When to read|
298
+ |---|---|
299
+ | `references/widget-meta-directives.md` | For `forEach` / `forItem` (iteration) and `if` (conditional rendering), including nested loops|
300
+ | `references/schema-from-lightning-type.md` | When `lightningTypeSchema` is provided; how to derive the widget `schema.json` from an Apex-backed Lightning Type|
301
+ | `references/widget-bundle-layout.md` | Folder layout, `-meta.xml` shape, `<pkgDir>` resolution rules|
302
+ | `references/widget-bundle-validation.md` | Step 9 — the checks to run before reporting |
303
+ | `references/widget-formulas.md` | Supported formula functions and operators|
304
+ | `examples/single-object.json` | Single-object pattern (root binding via `{!$attrs.X}`, no iteration)|
288
305
  | `examples/list-with-foreach.json` | Any list-iteration case — root-level collections, nested lists, and lists embedded inside a single-object widget (e.g. iterating a `List<InnerClass>` inside an outer Apex payload) |
289
- | `examples/conditional.json` | Conditional pattern (`if` on `meta`, including `if` + `forEach` together) |
290
- | `examples/formulas.json` | Formula usage — text/arithmetic/date transforms, `IF`/`AND`/`NOT` driving both a `tile/text` and `meta.if` |
306
+ | `examples/conditional.json` | Conditional pattern (`if` on `meta`, including `if` + `forEach` together)|
307
+ | `examples/formulas.json` | Formula usage — text/arithmetic/date transforms, `IF`/`AND`/`NOT` driving both a `tile/text` and `meta.if`|
@@ -0,0 +1,14 @@
1
+ # Widget Bundle Validation Checks
2
+
3
+ Report each check individually as `pass` or `fail (<reason>)`.
4
+
5
+ - **`schema-parses`** — `<pkgDir>/uiWidgets/<widgetName>/schema.json` parses as JSON.
6
+ - **`schema-root-keys`** — root has `title` (string), `type: "object"`, and `properties.attributes` (object) — where `properties.attributes` carries `lightning:type: "lightning__objectType"` and a nested `properties` map. No `unevaluatedProperties: false`.
7
+ - **`schema-leaf-types`** — every leaf under `properties.attributes.properties` carries a `lightning:type`. Singular nested inner-class fields carry `lightning:type` set to the inner Apex class reference (`@apexClassType/<namespace>__<OuterClass>$<InnerClass>`); the nested shape is not redeclared. `List<InnerClass>` fields carry `lightning:type: "lightning__listType"` with `items.lightning:type` set to the inner Apex class reference (`@apexClassType/<namespace>__<OuterClass>$<InnerClass>`), not a redeclared field map — see `references/schema-from-lightning-type.md`. **When the list has no Apex-backed type** (schema inferred from the prompt), `items.lightning:type: "lightning__objectType"` MUST carry an inline nested `properties` map for every item field the body binds via `{!$item.X}`.
8
+ - **`bindings-resolve`** — every `{!$attrs.X}` (or `{!$attrs.<outerField>.<innerField>}` for nested objects) in `<widgetName>.json` resolves to a property under `schema.json` `properties.attributes.properties`, and every `{!$item.X}` resolves to a `forItem` loop variable defined upstream. This applies to `$attrs`/`$item` references **inside formula expressions** too.
9
+ - **`formulas-supported`** — every function name used inside a `{!...}` expression appears in the *Supported functions* list in `references/widget-formulas.md`.
10
+ - **`body-envelope`** — `<widgetName>.json` root has `type: "lightning__agentforceWidget"` and a `contentBody` object whose `widgetBody` carries the UEM tree rooted at `tile/widget`. No node in the tree — root or non-root — carries a `type` key.
11
+ - **`metaxml-wellformed`** — `<widgetName>.uiwidget-meta.xml` parses as well-formed XML.
12
+ - **`metaxml-elements`** — `<widgetName>.uiwidget-meta.xml` has root `<UiWidgetBundle>` and contains `<masterLabel>` (non-empty), `<description>` (non-empty), and `<widgetType>JSON</widgetType>`.
13
+ - **`files-present`** — all three files exist at the resolved `<pkgDir>/uiWidgets/<widgetName>/` path.
14
+ - **`button-actions-present`** — every `tile/button` in `<widgetName>.json` has an `actions.click` action node whose `definition` is `action/openLink` or `action/sendMessage`.