@salesforce/afv-skills 1.40.0 → 1.41.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 (136) hide show
  1. package/package.json +1 -1
  2. package/skills/automation-flow-generate/SKILL.md +32 -43
  3. package/skills/experience-portal-create/SKILL.md +497 -0
  4. package/skills/experience-portal-create/assets/report-template.md +30 -0
  5. package/skills/experience-portal-create/references/mcp-invocation.md +288 -0
  6. package/skills/experience-portal-create/references/post-creation-activate-publish.md +165 -0
  7. package/skills/experience-portal-create/references/templates.md +253 -0
  8. package/skills/experience-ui-bundle-features-generate/SKILL.md +5 -1
  9. package/skills/experience-ui-bundle-frontend-generate/SKILL.md +2 -0
  10. package/skills/experience-ui-bundle-frontend-generate/references/page.md +1 -0
  11. package/skills/platform-datamask-run/SKILL.md +345 -0
  12. package/skills/platform-datamask-run/references/api-surface.md +130 -0
  13. package/skills/platform-datamask-run/references/policy-authoring.md +185 -0
  14. package/skills/platform-datamask-run/references/run-and-abort.md +116 -0
  15. package/skills/platform-datamask-run/scripts/poll-job.sh +115 -0
  16. package/skills/platform-dataspace-access-configure/SKILL.md +51 -3
  17. package/skills/platform-dataspace-access-configure/scripts/inspect-dataspace-scopes.sh +56 -0
  18. package/skills/platform-lightning-type-widget-coordinate/references/build-plan-format.md +1 -0
  19. package/skills/platform-sandbox-configure/SKILL.md +17 -2
  20. package/skills/platform-trial-org-create/SKILL.md +175 -0
  21. package/skills/platform-trial-org-create/examples/create_request.json +9 -0
  22. package/skills/platform-trial-org-create/examples/error_response.json +41 -0
  23. package/skills/platform-trial-org-create/examples/success_response.json +27 -0
  24. package/skills/platform-trial-org-create/references/error_codes.md +42 -0
  25. package/skills/platform-trial-org-create/references/signup_request_fields.md +69 -0
  26. package/skills/platform-trial-org-create/scripts/create_signup_request.sh +175 -0
  27. package/skills/platform-trial-org-create/scripts/get_signup_request.sh +155 -0
  28. package/skills/platform-widget-generate/SKILL.md +47 -6
  29. package/skills/platform-widget-generate/examples/conditional.json +3 -3
  30. package/skills/platform-widget-generate/examples/list-with-foreach.json +2 -2
  31. package/skills/platform-widget-generate/examples/single-object.json +2 -2
  32. package/skills/platform-widget-generate/references/widget-bundle-layout.md +1 -1
  33. package/skills/service-agentforce-channel-configure/SKILL.md +271 -0
  34. package/skills/service-agentforce-channel-configure/references/agent-wiring.md +97 -0
  35. package/skills/service-agentforce-channel-configure/references/channel-branch-email.md +145 -0
  36. package/skills/service-agentforce-channel-configure/references/channel-branch-voice.md +69 -0
  37. package/skills/service-agentforce-channel-configure/references/channel-types.md +61 -0
  38. package/skills/service-agentforce-channel-configure/references/live-traffic-gate.md +86 -0
  39. package/skills/service-agentforce-channel-configure/references/queue-resolution.md +135 -0
  40. package/skills/service-agentforce-channel-configure/references/routing-flow.md +384 -0
  41. package/skills/service-catalog-template-deploy/SKILL.md +310 -0
  42. package/skills/service-catalog-template-deploy/references/cli-invocation.md +258 -0
  43. package/skills/service-catalog-template-deploy/scripts/activate-verify.mjs +164 -0
  44. package/skills/service-catalog-template-deploy/scripts/build-deploy-payload.mjs +94 -0
  45. package/skills/service-catalog-template-deploy/scripts/resolve-template.mjs +331 -0
  46. package/skills/service-catalog-template-search/SKILL.md +212 -0
  47. package/skills/service-catalog-template-search/references/cli-invocation.md +128 -0
  48. package/skills/service-catalog-template-search/scripts/classify-catalog.mjs +205 -0
  49. package/skills/service-concierge-portal-generate/SKILL.md +126 -0
  50. package/skills/service-concierge-portal-generate/references/portal-deploy-runbook.md +1428 -0
  51. package/skills/service-digital-engagement-channel-configure/SKILL.md +46 -6
  52. package/skills/service-digital-engagement-channel-configure/assets/messaging_channel_template.xml +2 -1
  53. package/skills/service-digital-engagement-channel-configure/examples/asa_agent_channel.xml +4 -1
  54. package/skills/service-helpagent-coordinate/README.md +8 -2
  55. package/skills/service-helpagent-coordinate/SKILL.md +126 -130
  56. package/skills/service-helpagent-coordinate/assets/help-agent-spec.md +70 -53
  57. package/skills/service-helpagent-coordinate/references/agent-script.md +571 -457
  58. package/skills/service-helpagent-coordinate/references/channel-voice.md +38 -9
  59. package/skills/service-helpagent-coordinate/references/channel-web-chat.md +173 -49
  60. package/skills/service-helpagent-coordinate/references/output-report-format.md +126 -0
  61. package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +153 -0
  62. package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +79 -0
  63. package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +35 -0
  64. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/SKILL.md +271 -0
  65. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/references/cli-invocation.md +265 -0
  66. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/scripts/classify-enable-plan.mjs +220 -0
  67. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/scripts/classify-final-report.mjs +102 -0
  68. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/scripts/record-enable-result.mjs +73 -0
  69. package/skills/service-itsm-agentic-setup-agentforce-studio-validate/SKILL.md +206 -0
  70. package/skills/service-itsm-agentic-setup-agentforce-studio-validate/references/cli-invocation.md +194 -0
  71. package/skills/service-itsm-agentic-setup-agentforce-studio-validate/scripts/classify-readiness.mjs +223 -0
  72. package/skills/service-itsm-agentic-setup-cmdb-configure/SKILL.md +45 -7
  73. package/skills/service-itsm-agentic-setup-cmdb-configure/references/mcp-invocation.md +45 -5
  74. package/skills/service-itsm-agentic-setup-configure/SKILL.md +116 -0
  75. package/skills/service-itsm-agentic-setup-configure/examples/output-templates.md +64 -0
  76. package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +158 -0
  77. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/cli-invocation.md +361 -0
  78. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/error-taxonomy.md +44 -0
  79. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/reactivation.md +66 -0
  80. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +66 -0
  81. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/specialized-templates.md +148 -0
  82. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/workflow-detail.md +169 -0
  83. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/build-create-body.mjs +116 -0
  84. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/classify-agent-existence.mjs +185 -0
  85. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/classify-preflight.mjs +168 -0
  86. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/create-scratch-dir.mjs +58 -0
  87. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +197 -0
  88. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +186 -0
  89. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/action-availability.md +51 -0
  90. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/cli-invocation.md +345 -0
  91. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/error-taxonomy.md +44 -0
  92. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/reactivation.md +63 -0
  93. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +44 -0
  94. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/workflow-detail.md +149 -0
  95. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/build-create-body.mjs +110 -0
  96. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-action-availability.mjs +201 -0
  97. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-activate-result.mjs +135 -0
  98. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-agent-existence.mjs +194 -0
  99. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-preflight.mjs +158 -0
  100. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/create-scratch-dir.mjs +58 -0
  101. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +191 -0
  102. package/skills/service-itsm-agentic-setup-incident-management/SKILL.md +133 -0
  103. package/skills/service-itsm-agentic-setup-incident-management/examples/output-templates.md +71 -0
  104. package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +308 -0
  105. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone.json +23 -0
  106. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +193 -0
  107. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +57 -0
  108. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +394 -0
  109. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/SKILL.md +266 -0
  110. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/cli-invocation.md +106 -0
  111. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/helper-contracts.md +142 -0
  112. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/permset-topology.md +82 -0
  113. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/scripts/classify-action-surface.mjs +137 -0
  114. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/scripts/classify-assignment-state.mjs +99 -0
  115. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/scripts/classify-permset-availability.mjs +120 -0
  116. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/scripts/resolve-target-user.mjs +86 -0
  117. package/skills/service-itsm-agentic-setup-uel-user-create/SKILL.md +284 -0
  118. package/skills/service-itsm-agentic-setup-uel-user-create/references/mcp-invocation.md +302 -0
  119. package/skills/service-itsm-channels-coordinate/SKILL.md +472 -0
  120. package/skills/service-itsm-incident-mgmt-configure/SKILL.md +212 -0
  121. package/skills/service-itsm-incident-mgmt-configure/references/mcp-invocation.md +225 -0
  122. package/skills/service-itsm-incident-priority-configure/SKILL.md +53 -12
  123. package/skills/service-itsm-swarming-configure/SKILL.md +212 -0
  124. package/skills/service-itsm-teams-configure/SKILL.md +395 -0
  125. package/skills/service-itsm-teams-configure/references/azure-credential-population.md +213 -0
  126. package/skills/service-itsm-teams-configure/references/gotchas.md +23 -0
  127. package/skills/service-itsm-teams-coordinate/SKILL.md +175 -0
  128. package/skills/service-itsm-teams-coordinate/examples/output-templates.md +85 -0
  129. package/skills/service-itsm-teams-debug/SKILL.md +144 -0
  130. package/skills/service-itsm-teams-debug/references/configuration-checklists.md +277 -0
  131. package/skills/service-itsm-teams-debug/references/report-generation.md +95 -0
  132. package/skills/service-itsm-teams-employee-agent-configure/SKILL.md +139 -0
  133. package/skills/service-itsm-teams-employee-agent-configure/assets/Teams_AgentForce.EmbeddedServiceConfig-meta.xml +43 -0
  134. package/skills/service-itsm-teams-employee-agent-configure/references/teams-embedded-employee-agent.md +480 -0
  135. package/skills/service-itsm-teams-itdesk-configure/SKILL.md +232 -0
  136. package/skills/service-itsm-teams-itservice-configure/SKILL.md +391 -0
@@ -0,0 +1,266 @@
1
+ ---
2
+ name: service-itsm-agentic-setup-itsm-agentforce-permset-assign
3
+ description: "Resolve missing ITSM Intelligence invocable actions so a Fulfiller NGA agent can activate. Reads which of the four Core Fulfiller persona permsets (IncidentFulfiller, ProblemFulfillerPermSet, ChangeRequestFulfillerPermSet, ReleaseManagerPermSet) are provisioned, then assigns the running user the selected persona (plus backing PSL when license-gated) so svc_itsm_intelligence__* actions surface; hands off to service-itsm-agentic-setup-agentforce-studio-validate if none are provisioned. Use when the Fulfiller agent-configure skill reports missing actions on activate, when 'Invocable action svc_itsm_intelligence__X does not exist' surfaces, or when a user asks to grant themselves Fulfiller prompt-template access. DO NOT TRIGGER for Employee-agent access, Agentforce for IT Service toggles, agent creation, CMDB access, or generic permset assignment."
4
+ metadata:
5
+ version: "1.0"
6
+ domains: ["Service", "Agentforce"]
7
+ minApiVersion: "67.0"
8
+ relatedSkills:
9
+ - "dx-org-permission-set-assign"
10
+ - "service-itsm-agentic-setup-agentforce-studio-validate"
11
+ - "service-itsm-agentic-setup-cmdb-access-assign"
12
+ - "service-itsm-agentic-setup-fulfiller-agent-configure"
13
+ cliTools:
14
+ - tool: ["node"]
15
+ semver: ">=18.0.0"
16
+ - tool: ["sf"]
17
+ semver: ">=2.0.0"
18
+ accessCheck:
19
+ - type: "license"
20
+ value: "Agentforce"
21
+ allowed-tools: |
22
+ Bash
23
+ Read
24
+ AskUserQuestion
25
+ ---
26
+
27
+ # Assign an ITSM Fulfiller Persona Permission Set (Prompt-Template Access)
28
+
29
+ Grants the **running user** one of the Core-shipped **Fulfiller persona permission sets** that expose the `svc_itsm_intelligence__*` prompt-template **invocable actions** on the target org — the actions the Fulfiller NGA agent scripts reference via `source:` / `target: generatePromptResponse://...`. When those invocables are not surfaced by `/services/data/v67.0/actions/custom/generatePromptResponse` for the running user, the Fulfiller agent-configure skill's Phase 6 `activate` call returns **HTTP 200** with a silent `{success:false, messages:[{... "does not exist"}]}` body and the agent never becomes usable. This skill fixes that gap by assigning the correct Fulfiller persona permset (and its backing license when one exists) — **or**, when no Fulfiller persona permset is provisioned on the org at all, hands off to the Agentforce Studio configure/validate skill so the ITSM AddOn(s) can be enabled first.
30
+
31
+ The four Fulfiller persona permsets, their AddOns, PSLs, and the userPerms they grant are documented in `references/permset-topology.md`. All are Core-shipped in namespace `force` — there is no managed-package namespaced permset for this feature.
32
+
33
+ **Employee agent is out of scope.** The Employee NGA agent's access model is separate (org-preferences + a different persona layer) and does not map onto these four persona permsets.
34
+
35
+ Every call runs through the **Salesforce CLI (`sf`)**:
36
+ - `sf api request rest` — authenticated Connect API GET (identity, verify read).
37
+ - `sf data query` — SOQL on `PermissionSet` (persona presence), `PermissionSetAssignment` / `PermissionSetLicenseAssign` (idempotency).
38
+ - `sf org assign permset` — assigning the permission set for the running user.
39
+ - No token is ever extracted; no MCP is used.
40
+
41
+ ## Scope
42
+
43
+ - **In scope**: detecting which of the four Fulfiller persona permsets (`IncidentFulfiller`, `ProblemFulfillerPermSet`, `ChangeRequestFulfillerPermSet`, `ReleaseManagerPermSet`) are provisioned on the org, letting the user pick which persona to assign, checking existing assignments, assigning the permission-set license (when the persona is license-gated) and permission set to the running user (or a named user), verifying the target `svc_itsm_intelligence__*` invocable actions surface via a follow-up `/actions/custom/generatePromptResponse` read.
44
+ - **Out of scope**: Employee-agent access (different access model, different skill), installing/enabling the ITSM AddOn(s) or content bundle (hand off to `service-itsm-agentic-setup-agentforce-studio-validate`), enabling org-level Agentforce feature toggles, creating a permission set, creating or activating the Fulfiller agent (that's `service-itsm-agentic-setup-fulfiller-agent-configure`), CMDB access (`service-itsm-agentic-setup-cmdb-access-assign`), generic non-ITSM permission-set assignment (`dx-org-permission-set-assign`).
45
+
46
+ ## Mechanism
47
+
48
+ Two branches, decided by a read-only detection step first:
49
+
50
+ - **Branch A — one or more Fulfiller persona permsets exist on the org.** Ask the user which persona to assign (do not auto-select — a Fulfiller commonly needs only one). Idempotent assign: PSL first when the persona is license-gated, then permission set, verified by read-back and by a follow-up `/actions/custom/generatePromptResponse` read.
51
+ - **Branch B — none of the four Fulfiller persona permsets exist on the org.** The ITSM AddOn(s) are not provisioned; permset-assign is a no-op. STOP and hand off to `service-itsm-agentic-setup-agentforce-studio-validate` so the AddOn(s) can be enabled first.
52
+
53
+ The two-branch shape is deliberate — the failure signature ("`svc_itsm_intelligence__X` does not exist" on activate) looks identical whether a persona is present-and-unassigned or the AddOn is absent entirely, and there is no way to tell from the activate response alone. The pre-check on the four persona `PermissionSet` names is what disambiguates them.
54
+
55
+ ## Four helper scripts (all invoked via `Bash`) hold every deterministic decision (A9). Full I/O contracts in `references/helper-contracts.md`; workflow-level usage summarized below:
56
+
57
+ - `scripts/classify-permset-availability.mjs` — Branch A vs B and the per-persona `needsPsl` flag. Returns the full `candidates[]` (personas actually on the org) for the caller to prompt on.
58
+ - `scripts/resolve-target-user.mjs` — extracts `005…` running-user Id from the API-root `identity` URL. Fails closed on any malformed shape.
59
+ - `scripts/classify-assignment-state.mjs` — idempotency; pass the sentinel `NO-PSL` in place of the PSLA path when the selected persona's `needsPsl:false`.
60
+ - `scripts/classify-action-surface.mjs` — Phase 4 verify verdict from the `/actions/custom/generatePromptResponse` capture (with optional expected-actions CSV).
61
+
62
+ ---
63
+
64
+ ## Preconditions
65
+
66
+ 1. **`sf` CLI installed and authenticated to the target org** (`sf org display -o <alias>` shows Connected). All calls use `--target-org <alias>`; never extract or pass the access token by hand.
67
+ 2. **API v67.0+**.
68
+ 3. **`node` ≥ 18** on PATH.
69
+
70
+ If a precondition fails, `sf` surfaces an auth or `401`/`403`/`404`; report the raw response verbatim and stop.
71
+
72
+ ---
73
+
74
+ ## Clarifying questions
75
+
76
+ Ask only what cannot be inferred from conversation:
77
+
78
+ - **Target org** — the `sf` alias. Default to `sf config get target-org` if unset.
79
+ - **Target user** — default to the **running user** (resolved via `scripts/resolve-target-user.mjs`). If the user asks to assign on behalf of a named user, resolve them by `Username` first.
80
+ - **Which Fulfiller persona?** Incident / Problem / Change / Release. Only ask about personas that are actually provisioned on the org (from `candidates[]`). Do not auto-select — a Fulfiller commonly needs only one persona (e.g. Incident) even when others are provisioned.
81
+ - **Confirm the write** — assigning a permission-set license consumes a seat and takes effect for a live user session. Present the target user + org + persona permset name, and require an explicit "yes" via `AskUserQuestion` before writing.
82
+
83
+ ---
84
+
85
+ ## Workflow
86
+
87
+ All calls go through `sf`; substitute `<alias>` with the target org.
88
+
89
+ ### Phase 1 — Read: which Fulfiller persona permsets are provisioned on this org?
90
+
91
+ 1. Query `PermissionSet` for the four known Fulfiller persona DeveloperNames:
92
+
93
+ ```bash
94
+ sf data query \
95
+ -q "SELECT Id, Name, Label, LicenseId FROM PermissionSet WHERE Name IN ('IncidentFulfiller','ProblemFulfillerPermSet','ChangeRequestFulfillerPermSet','ReleaseManagerPermSet')" \
96
+ --target-org <alias> --json > /tmp/itsm-personas.json 2>/tmp/itsm-personas.err || true
97
+ ```
98
+
99
+ (The `PermissionSet` namespace on all four is `force` — do NOT filter by `NamespacePrefix`.)
100
+
101
+ 2. Classify:
102
+
103
+ ```bash
104
+ node "<skill_dir>/scripts/classify-permset-availability.mjs" /tmp/itsm-personas.json
105
+ ```
106
+
107
+ The classifier prints `{ personasFound, personasMissing, candidates, verdict, reasons }`, where each `candidates[]` row is `{Id, Name, Label, LicenseId, needsPsl}`:
108
+ - `verdict:"ASSIGN"` (≥1 persona present) ⇒ continue to Phase 2. Present the `personasFound` list to the user via `AskUserQuestion` and get the selected persona; record its `Id`, `LicenseId`, and `needsPsl` — they drive whether Phase 2b/2d touch the PSL at all.
109
+ - `verdict:"HAND-OFF"` (none of the four personas present) ⇒ Phase 2 is impossible on this org; go to Phase 3 (Branch B hand-off).
110
+ - `verdict:"CANNOT-CONFIRM"` (query failed) ⇒ surface the raw CLI error verbatim; stop.
111
+
112
+ ### Phase 2 — Assign path (Branch A)
113
+
114
+ 2a. **Resolve the target user.** Read the identity URL, then extract the user Id via the resolver (do NOT parse the URL by hand; do NOT use `USER_ID()` — Apex-only, rejected by REST; do NOT rely on `/chatter/users/me` — `403` when Chatter is off):
115
+
116
+ ```bash
117
+ sf api request rest "/services/data/v67.0/" --method GET --target-org <alias> > /tmp/api-root.json 2>/tmp/api-root.err || true
118
+ node "<skill_dir>/scripts/resolve-target-user.mjs" /tmp/api-root.json
119
+ ```
120
+
121
+ The resolver prints `{ userId, identity, verdict, reasons }`. On `verdict:"RESOLVED"` use `userId` as the running user; on `verdict:"CANNOT-CONFIRM"` surface the reasons verbatim and stop — do NOT guess.
122
+
123
+ If the user asks to assign on behalf of a **named** user instead, resolve by `Username`:
124
+
125
+ ```bash
126
+ sf data query \
127
+ -q "SELECT Id, Username, Name, IsActive FROM User WHERE Username = '<username>'" \
128
+ --target-org <alias> --json > /tmp/user-lookup.json 2>/tmp/user-lookup.err || true
129
+ ```
130
+
131
+ 2b. **Idempotency read.** Query the `PermissionSetAssignment` for the target user + selected persona's `Id` (SOQL shape in `references/cli-invocation.md`), and then branch on the selected persona's `needsPsl`:
132
+
133
+ - **`needsPsl:true`** — query the `PermissionSetLicenseAssign` for the target user + the persona's `LicenseId`, then classify:
134
+
135
+ ```bash
136
+ node "<skill_dir>/scripts/classify-assignment-state.mjs" /tmp/psa-existing.json /tmp/psla-existing.json
137
+ ```
138
+
139
+ - **`needsPsl:false`** — skip the PSLA query entirely; pass the sentinel:
140
+
141
+ ```bash
142
+ node "<skill_dir>/scripts/classify-assignment-state.mjs" /tmp/psa-existing.json NO-PSL
143
+ ```
144
+
145
+ The classifier prints `{ permsetAssigned, licenseAssigned, needsWrite, verdict, reasons }`. If `needsWrite:false` ⇒ Phase 4 (verify only). Else continue to Phase 2c.
146
+
147
+ 2c. **Confirm-to-write checkpoint (REQUIRED).** Present the target user + org + persona permset name and require an explicit "yes" via `AskUserQuestion`. On "no", stop and report the current state without any writes.
148
+
149
+ 2d. **Assign** — order depends on the selected persona's `needsPsl`:
150
+
151
+ - **`needsPsl:true`** — POST the PSL to `/sobjects/PermissionSetLicenseAssign` FIRST, then run `sf org assign permset --name <permsetName> --on-behalf-of <userId>`. Assigning the permission set without the PSL sticks the assignment but the license backing it never activates. Exact call shapes: `references/cli-invocation.md`.
152
+ - **`needsPsl:false`** — SKIP the PSL POST entirely; run `sf org assign permset` only. (This is normal for `ReleaseManagerPermSet` — its Fulfiller gating is on the org-preference layer, not on a PSL. Assigning the permset is still the correct write.)
153
+
154
+ Response handling:
155
+ - `201` on POST / `success:true` on `sf org assign permset` ⇒ assigned.
156
+ - `400 DUPLICATE_VALUE` on the PSL POST ⇒ user already had it; treat as success, not error.
157
+ - `400 INSUFFICIENT_ACCESS` / seat-exhaustion on the PSL POST ⇒ STOP for this write; tell the user the PSL has no seats available.
158
+
159
+ ### Phase 3 — Hand-off path (Branch B: no Fulfiller persona provisioned)
160
+
161
+ 3. When Phase 1 returns `verdict:"HAND-OFF"`, none of the four Fulfiller persona permsets exist on this org — the ITSM AddOn(s) are not provisioned. Permset-assign is a no-op in this state. Present the discovery via `AskUserQuestion`:
162
+
163
+ _"None of the Fulfiller persona permission sets (Incident, Problem, Change, Release) is provisioned on this org — no permset can grant access to actions that don't exist yet. Run `service-itsm-agentic-setup-agentforce-studio-validate` to diagnose which AddOn needs enabling?"_ (options: **Yes, run the readiness check** / **No, stop here**).
164
+
165
+ - On **Yes**: delegate to `service-itsm-agentic-setup-agentforce-studio-validate` and let it recommend the configure/bundle-deploy skill.
166
+ - On **No**: stop and report the current state (no persona provisioned, cannot assign) — no writes.
167
+
168
+ ### Phase 4 — Verify
169
+
170
+ 4. Regardless of write vs skip, re-read `/actions/custom/generatePromptResponse` and classify via the helper (never by prose grep — A9). CSV shape and both invocation forms live in `references/helper-contracts.md` / `references/cli-invocation.md`:
171
+
172
+ ```bash
173
+ sf api request rest "/services/data/v67.0/actions/custom/generatePromptResponse" \
174
+ --method GET --target-org <alias> > /tmp/generate-prompt-response.json 2>/tmp/generate-prompt-response.err || true
175
+ node "<skill_dir>/scripts/classify-action-surface.mjs" /tmp/generate-prompt-response.json [expectedActions-csv]
176
+ ```
177
+
178
+ The helper prints `{ present, missing, totalItsmActionsSeen, verdict, reasons }`. On `SURFACED` proceed to Phase 5; on `PARTIAL` / `MISSING` after a successful assign, tell the user the write succeeded but the actions are not surfaced — session refresh or wrong persona. Do not falsely report success. On `CANNOT-CONFIRM` surface the reasons verbatim.
179
+
180
+ ### Phase 5 — Aggregate verdict
181
+
182
+ 5. Report one of:
183
+ - **ASSIGNED** — Branch A wrote, verify saw the target actions surface.
184
+ - **ALREADY-ASSIGNED** — Branch A found `needsWrite:false`; verify saw the target actions surface. (Assignment is already in place; no writes needed.)
185
+ - **HAND-OFF** — Branch B; no Fulfiller persona is provisioned. Named the follow-up skill.
186
+ - **VERIFY-INCONCLUSIVE** — write completed but the verify read didn't surface the expected action set. Surface the observed state verbatim; do not report success.
187
+ - **FAILED** — any Phase 2d write returned an error other than `DUPLICATE_VALUE`. Report the raw error.
188
+
189
+ ---
190
+
191
+ ## Rules / Constraints
192
+
193
+ | Constraint | Rationale |
194
+ |-----------|-----------|
195
+ | Detect Fulfiller persona presence via the four fixed `PermissionSet.Name` values BEFORE any permset write | The failure signature ("action does not exist" on activate) is identical for AddOn-absent and permset-not-assigned; only the pre-check disambiguates them. The four personas are Core-shipped in namespace `force` — a `NamespacePrefix` filter never returns them |
196
+ | Ask the user which persona to assign — never auto-select | A Fulfiller commonly needs only one persona (e.g. Incident). Auto-assigning the first row returned would over-grant |
197
+ | All decisions are made by helper scripts, never by prose | Assignment/idempotency logic is deterministic; prose interpretation is not (A9) |
198
+ | Assign the PSL before the permission set — ONLY when the selected persona's `needsPsl:true` | The permission set is license-backed; the license seat must be held before the assignment sticks. When the selected persona has no `LicenseId`, `needsPsl:false` and the PSL POST is skipped entirely |
199
+ | Read `needsPsl` from the SELECTED persona's `LicenseId`, per-row — never from a namespace-wide PSL query | Different personas can have different license shapes on the same org; falling back to a namespace-wide PSL would POST a wrong `PermissionSetLicenseAssign` |
200
+ | Resolve the running-user Id via `scripts/resolve-target-user.mjs` — never by prose parsing the `identity` URL | The identity URL's segment shape (`005…`, 15 or 18 chars) is a hard rule; the classifier validates and fails closed |
201
+ | Classify the Phase 4 action surface via `scripts/classify-action-surface.mjs` — never by prose grep of the response | Verify is the gate for reporting SUCCESS vs VERIFY-INCONCLUSIVE; the decision must be deterministic |
202
+ | Treat `400 DUPLICATE_VALUE` on PSL POST as success | It means the user already has that assignment — idempotent, not an error |
203
+ | Never create or edit a permission set | This skill only assigns the standard Fulfiller persona permission set(s); authoring perm sets is out of scope |
204
+ | Never install the AddOn / never toggle org-level Agentforce features | That is the `service-itsm-agentic-setup-agentforce-studio-validate` / `-configure` scope; this skill hands off, it does not enable |
205
+ | Verify after write via `/actions/custom/generatePromptResponse` — never trust POST return code alone | The assignment can succeed while the target action surface still doesn't include what the Fulfiller template needs (wrong persona, cache) |
206
+ | Confirm-to-write checkpoint before Phase 2d | A permset assign consumes a license seat and takes effect for a live user session |
207
+ | Never extract the access token | Use `sf api request rest` / `sf data query` / `sf org assign permset` — they use the CLI's stored session |
208
+ | Report exact error text from the CLI response | Enables support to diagnose failures |
209
+
210
+ ---
211
+
212
+ ## Verification Checklist
213
+
214
+ - [ ] Persona availability classified by `scripts/classify-permset-availability.mjs` against the four fixed persona Names — never by prose scanning the query output.
215
+ - [ ] User was asked to pick a persona from `personasFound[]` — no auto-selection.
216
+ - [ ] Target user Id resolved by `scripts/resolve-target-user.mjs` — never by prose splitting the identity URL.
217
+ - [ ] The SELECTED persona's `needsPsl` drove Phase 2b/2d: PSL SOQL + POST were performed when `true` and skipped when `false`.
218
+ - [ ] `permsetLicenseId` used for the PSL POST came from the SELECTED persona's own `LicenseId` — never a fallback from a namespace-wide query.
219
+ - [ ] On Branch A: existing assignments read via `sf data query` before any write, classified by `scripts/classify-assignment-state.mjs` (with `NO-PSL` sentinel when `needsPsl:false`).
220
+ - [ ] On Branch A: user confirmed the write at the Phase 2c checkpoint.
221
+ - [ ] On Branch A + `needsPsl:true`: PSL was POSTed before the permission set was assigned.
222
+ - [ ] `DUPLICATE_VALUE` on the PSL POST was treated as success, not failure.
223
+ - [ ] On Branch B: no write was attempted; the hand-off to `service-itsm-agentic-setup-agentforce-studio-validate` was offered.
224
+ - [ ] Phase 4 verify classified via `scripts/classify-action-surface.mjs` — no false ASSIGNED without the helper returning `verdict:"SURFACED"`.
225
+ - [ ] Aggregate verdict reported (ASSIGNED / ALREADY-ASSIGNED / HAND-OFF / VERIFY-INCONCLUSIVE / FAILED).
226
+
227
+ ---
228
+
229
+ ## Output Format
230
+
231
+ ```text
232
+ ITSM Fulfiller Persona Permset Assignment (via service-itsm-agentic-setup-itsm-agentforce-permset-assign)
233
+
234
+ Org: <org-alias> (API v67.0)
235
+ Target user: <username> (<userId>)
236
+ Persona: <Incident | Problem | Change | Release>
237
+ PermSet: <DeveloperName>
238
+
239
+ Personas provisioned on org ...... <comma-separated list | none>
240
+ Existing PSL assignment .......... <yes | no | n/a>
241
+ Existing permset assignment ...... <yes | no>
242
+ Write PSL ........................ <succeeded | already-had | skipped | FAILED>
243
+ Write permset .................... <succeeded | already-had | skipped | FAILED>
244
+ Verify actions surface ........... <yes | partial | no>
245
+
246
+ Verdict: ASSIGNED | ALREADY-ASSIGNED | HAND-OFF | VERIFY-INCONCLUSIVE | FAILED
247
+ Reason: <plain-language explanation, or empty on success>
248
+
249
+ Next steps:
250
+ - <If ASSIGNED / ALREADY-ASSIGNED: "Re-run service-itsm-agentic-setup-fulfiller-agent-configure — the invocable actions should now surface, and the activate call will succeed.">
251
+ - <If HAND-OFF: "No Fulfiller persona is provisioned on this org. Run service-itsm-agentic-setup-agentforce-studio-validate to identify which AddOn needs enabling.">
252
+ - <If VERIFY-INCONCLUSIVE: list the observed state verbatim; a session refresh or a different persona may be required>
253
+ - <If FAILED: list the observed error verbatim + remediation>
254
+ ```
255
+
256
+ Keep internal jargon (record Ids, HTTP status codes, `FUNCTIONALITY_NOT_ENABLED`, `DUPLICATE_VALUE`, object/developer names, `sf api request rest`) out of user-facing output.
257
+
258
+ ---
259
+
260
+ ## Reference File Index
261
+
262
+ | File | When to read |
263
+ |------|--------------|
264
+ | `references/permset-topology.md` | Any change to the persona list — the four Core-shipped Fulfiller permsets, their AddOns/PSLs/userPerms, and the fixed-lookup discovery query |
265
+ | `references/cli-invocation.md` | Every phase — exact `sf api request rest` / `sf data query` / `sf org assign permset` call shapes, the never-extract-token rule, response envelopes |
266
+ | `references/helper-contracts.md` | The input/output shapes of all four helper scripts (`classify-permset-availability.mjs`, `resolve-target-user.mjs`, `classify-assignment-state.mjs`, `classify-action-surface.mjs`) and how to interpret each verdict |
@@ -0,0 +1,106 @@
1
+ # CLI Invocation — service-itsm-agentic-setup-itsm-agentforce-permset-assign
2
+
3
+ Every read and write in this skill uses the **Salesforce CLI (`sf`)** against the CLI's stored session for `--target-org <alias>`. **Never extract the access token** from `sf org display` — hand-building a raw HTTP request bypasses the CLI session and leaks a bearer token into shell context.
4
+
5
+ ## `--json` rule
6
+
7
+ - `sf data query` **takes** `--json`. Results come back in a `{status, result: {records: [...]}}` envelope — that's what the classifier scripts expect.
8
+ - `sf api request rest` does **not** — its raw stdout body is already JSON. Passing `--json` here errors on some Connect endpoints.
9
+ - `sf org assign permset` **takes** `--json`. Its `result` field carries `{ successes: [...], failures: [...] }`.
10
+
11
+ ## Discovery read (Phase 1)
12
+
13
+ Fixed-lookup query over the four Core-shipped Fulfiller persona `PermissionSet.Name` values (see `permset-topology.md`). The namespace on all four is `force` — do NOT filter by `NamespacePrefix`.
14
+
15
+ ```bash
16
+ sf data query \
17
+ -q "SELECT Id, Name, Label, LicenseId \
18
+ FROM PermissionSet \
19
+ WHERE Name IN ( \
20
+ 'IncidentFulfiller', \
21
+ 'ProblemFulfillerPermSet', \
22
+ 'ChangeRequestFulfillerPermSet', \
23
+ 'ReleaseManagerPermSet' \
24
+ )" \
25
+ --target-org <alias> --json
26
+ ```
27
+
28
+ Interpret the result:
29
+
30
+ - **≥1 row** ⇒ Branch A (assign). Ask the user which persona to assign — do NOT auto-select.
31
+ - **Zero rows** ⇒ Branch B (hand-off). The ITSM AddOn(s) are not provisioned; route to `service-itsm-agentic-setup-agentforce-studio-validate`.
32
+
33
+ There is no separate `InstalledSubscriberPackage` or namespace-wide `PermissionSetLicense` query in Phase 1 — the persona lookup above is sufficient because Core AddOn provisioning materializes the persona `PermissionSet` rows directly.
34
+
35
+ ## Identity read (Phase 2a)
36
+
37
+ ```bash
38
+ sf api request rest "/services/data/v67.0/" --method GET --target-org <alias>
39
+ ```
40
+
41
+ Response `identity` is a URL ending in `/<orgId>/<userId>`. Take the last path segment starting with `005` as the running user Id via `scripts/resolve-target-user.mjs`. Do **not** call `USER_ID()` (Apex-only), `/chatter/users/me`, or `/connect/user-profiles/me` — those either fail against REST or 403 when Chatter/Communities is off.
42
+
43
+ For a named user, resolve by `Username`:
44
+
45
+ ```bash
46
+ sf data query -q "SELECT Id, Username, Name, IsActive FROM User WHERE Username = '<username>'" \
47
+ --target-org <alias> --json
48
+ ```
49
+
50
+ ## Idempotency reads (Phase 2b)
51
+
52
+ Substitute `<permsetId>` with the SELECTED persona's `Id` and `<pslId>` with the SELECTED persona's `LicenseId` (both from Phase 1's `candidates[]`).
53
+
54
+ ```bash
55
+ sf data query -q "SELECT Id FROM PermissionSetAssignment WHERE AssigneeId = '<userId>' AND PermissionSetId = '<permsetId>'" \
56
+ --target-org <alias> --json > /tmp/psa-existing.json
57
+
58
+ # Only when needsPsl:true on the selected persona:
59
+ sf data query -q "SELECT Id FROM PermissionSetLicenseAssign WHERE AssigneeId = '<userId>' AND PermissionSetLicenseId = '<pslId>'" \
60
+ --target-org <alias> --json > /tmp/psla-existing.json
61
+ ```
62
+
63
+ When the selected persona's `needsPsl:false` (e.g. `ReleaseManagerPermSet`), SKIP the PSLA query — pass the sentinel `NO-PSL` to `classify-assignment-state.mjs`.
64
+
65
+ ## Writes (Phase 2d)
66
+
67
+ Assign the PSL first, then the permission set — the license backs the permset, and the assignment is not fully effective until the seat is held. When `needsPsl:false`, skip the PSL POST entirely and only run `sf org assign permset`.
68
+
69
+ ```bash
70
+ # PSL first — POST to the sObject endpoint (no bulk API dependency). needsPsl:true only.
71
+ sf api request rest "/services/data/v67.0/sobjects/PermissionSetLicenseAssign" \
72
+ --method POST \
73
+ --body '{"AssigneeId":"<userId>","PermissionSetLicenseId":"<pslId>"}' \
74
+ --target-org <alias>
75
+ ```
76
+
77
+ Response envelope: `{"id":"0Pk...","success":true,"errors":[]}` on assignment; `[{"errorCode":"DUPLICATE_VALUE",...}]` when the user already holds it (treat as success).
78
+
79
+ ```bash
80
+ # Permset — sf CLI subcommand, supports --on-behalf-of for a named user.
81
+ # <permsetName> is the selected persona's DeveloperName (from candidates[].Name).
82
+ sf org assign permset --name "<permsetName>" \
83
+ --on-behalf-of <userId> \
84
+ --target-org <alias> --json
85
+ ```
86
+
87
+ Response envelope: `result.successes[]` and `result.failures[]`. A `failure.message` matching `Duplicate id` / `already has` is idempotent success.
88
+
89
+ ## Verify read (Phase 4)
90
+
91
+ ```bash
92
+ sf api request rest "/services/data/v67.0/actions/custom/generatePromptResponse" \
93
+ --method GET --target-org <alias>
94
+ ```
95
+
96
+ Response can be either `{actions: [ {name, ...}, ... ]}` (legacy) or `{actions: {<name>: {...}, ...}}` (newer). The Fulfiller agent-configure skill's Phase 1b provides the specific `svc_itsm_intelligence__*` names to look for; passed as the second arg to `classify-action-surface.mjs`.
97
+
98
+ ## Common error responses (surface verbatim)
99
+
100
+ | Status / errorCode | Meaning | Handling |
101
+ |---|---|---|
102
+ | `400 DUPLICATE_VALUE` on PSL POST | User already holds this PSL | Idempotent success; not a failure |
103
+ | `400 INSUFFICIENT_ACCESS` on PSL POST | PSL seat exhausted | STOP the write; tell the user how many seats are in use vs licensed |
104
+ | `401 Unauthorized` on any call | CLI session expired for the target alias | `sf org login web --alias <alias>` and retry |
105
+ | `403 FUNCTIONALITY_NOT_ENABLED` on the verify GET | The `generatePromptResponse` custom-action endpoint is gated on this user | Same signal as Branch A but for the discovery route — surface it and check whether the assigned persona permset actually gates the target actions |
106
+ | `404 Not Found` on `sf org assign permset` | Permset name does not exist on this org | Re-run Phase 1 discovery; the org state may have changed |
@@ -0,0 +1,142 @@
1
+ # Helper Script Contracts — service-itsm-agentic-setup-itsm-agentforce-permset-assign
2
+
3
+ All four scripts are deterministic, side-effect-free JSON classifiers (A9). They read one or more `sf data query --json` / `sf api request rest` captures and emit a single JSON object to stdout. Exit is `0` on any parseable body; the verdict is carried in the payload. Exit `2` is a usage error (missing args); the scripts do **not** exit non-zero on a workflow-blocking condition — that is the caller's job based on the emitted `verdict`.
4
+
5
+ ## `scripts/classify-permset-availability.mjs`
6
+
7
+ Decides Branch A vs Branch B (assign vs hand-off) AND emits per-persona `needsPsl` flags. Fulfiller-only — the four persona names are hard-coded.
8
+
9
+ ### Input
10
+
11
+ One file path (positional):
12
+
13
+ 1. `permsets.json` — `sf data query --json` capture of `PermissionSet` filtered to the four Fulfiller persona `Name` values (see `permset-topology.md`). Unrelated rows are filtered out by the classifier.
14
+
15
+ Must be the raw stdout capture (the `{status, result: {records: [...]}}` envelope). An unparseable / non-envelope body produces `verdict:"CANNOT-CONFIRM"` with the raw error in `reasons[]`.
16
+
17
+ ### Output
18
+
19
+ ```json
20
+ {
21
+ "personasFound": ["IncidentFulfiller"],
22
+ "personasMissing": ["ProblemFulfillerPermSet","ChangeRequestFulfillerPermSet","ReleaseManagerPermSet"],
23
+ "candidates": [
24
+ { "Id": "0PS...", "Name": "IncidentFulfiller", "Label": "Incident Fulfiller", "LicenseId": "0PL...", "needsPsl": true }
25
+ ],
26
+ "verdict": "ASSIGN",
27
+ "reasons": ["..."]
28
+ }
29
+ ```
30
+
31
+ - `candidates[]` lists ONLY rows whose `Name` matches one of the four known Fulfiller personas. Unrelated rows in the query result are dropped.
32
+ - `needsPsl` is derived per-row from `LicenseId !== null` — read it from the SELECTED persona in the workflow.
33
+ - `personasFound` preserves the canonical order (Incident, Problem, Change, Release) so the AskUserQuestion menu is stable across runs.
34
+
35
+ Verdict values:
36
+
37
+ | verdict | Meaning | Caller action |
38
+ |---|---|---|
39
+ | `ASSIGN` | ≥1 Fulfiller persona present on the org | Continue to Phase 2. AskUserQuestion which persona to assign; read `needsPsl` off that persona to gate PSL SOQL + POST |
40
+ | `HAND-OFF` | None of the four personas present | Skip Phase 2 entirely; go to Phase 3 (delegate to `service-itsm-agentic-setup-agentforce-studio-validate`) |
41
+ | `CANNOT-CONFIRM` | The query envelope was unparseable | Surface `reasons[]` verbatim and stop |
42
+
43
+ **Never auto-select** from `candidates[]` — a Fulfiller commonly needs only one persona (e.g. Incident) even when several are provisioned. Present the list and let the user pick.
44
+
45
+ ## `scripts/resolve-target-user.mjs`
46
+
47
+ Extracts the running user's Id from the API-root `identity` URL. Replaces any prose "take the last path segment starting with `005` ..." step (A9).
48
+
49
+ ### Input
50
+
51
+ One file path (positional):
52
+
53
+ 1. `api-root.json` — capture of `sf api request rest "/services/data/v67.0/" --method GET` for the target org.
54
+
55
+ ### Output
56
+
57
+ ```json
58
+ {
59
+ "userId": "005...",
60
+ "identity": "https://<mydomain>.my.salesforce.com/id/00D.../005...",
61
+ "verdict": "RESOLVED",
62
+ "reasons": ["..."]
63
+ }
64
+ ```
65
+
66
+ Validation: the trailing path segment of `identity` must match `^005[A-Za-z0-9]{12}([A-Za-z0-9]{3})?$` (15 or 18 chars, `005` prefix). Any other shape produces `CANNOT-CONFIRM` with the raw identity URL in `reasons[]`.
67
+
68
+ | verdict | Meaning | Caller action |
69
+ |---|---|---|
70
+ | `RESOLVED` | `userId` is safe to use as the running-user Id | Use `userId` for Phase 2b/2d assignments |
71
+ | `CANNOT-CONFIRM` | No identity URL, or trailing segment does not match `005…` | Surface `reasons[]` verbatim and stop; do NOT fall back to prose parsing |
72
+
73
+ ## `scripts/classify-assignment-state.mjs`
74
+
75
+ Decides whether the Phase 2d write is needed (idempotency). Handles both the license-backed persona flow and the standalone-persona flow.
76
+
77
+ ### Input
78
+
79
+ Two positional arguments:
80
+
81
+ 1. `psa-existing.json` — `sf data query --json` capture of `PermissionSetAssignment` for the target user + selected persona's `Id`.
82
+ 2. `psla-existing.json` **OR** the literal sentinel `NO-PSL`:
83
+ - If the selected persona's `needsPsl:true`, pass the PSLA capture path.
84
+ - If the selected persona's `needsPsl:false`, pass the string `NO-PSL` — the classifier skips the PSLA envelope read and reports `licenseAssigned:null`.
85
+
86
+ ### Output
87
+
88
+ ```json
89
+ {
90
+ "permsetAssigned": false,
91
+ "licenseAssigned": true,
92
+ "needsWrite": true,
93
+ "verdict": "NEEDS-WRITE",
94
+ "reasons": ["...", "..."]
95
+ }
96
+ ```
97
+
98
+ `licenseAssigned` is `null` (not `false`) in the `NO-PSL` branch to make it distinguishable from a real "no PSL row found" case.
99
+
100
+ Verdict values:
101
+
102
+ | verdict | Meaning | Caller action |
103
+ |---|---|---|
104
+ | `NEEDS-WRITE` | `permsetAssigned` is `false` (or, when `needsPsl:true`, `licenseAssigned` is also `false`) | Confirm-to-write (Phase 2c), then Phase 2d (PSL first + permset if `needsPsl:true`; permset only if `needsPsl:false`) |
105
+ | `ALREADY-ASSIGNED` | All applicable rows present | Skip Phase 2d entirely; go to Phase 4 verify |
106
+ | `CANNOT-CONFIRM` | One of the query envelopes was unparseable | Surface `reasons[]` verbatim and stop |
107
+
108
+ ## `scripts/classify-action-surface.mjs`
109
+
110
+ Decides whether the target `svc_itsm_intelligence__*` invocable actions are surfaced for the running user after the assign. Replaces prose "grep the response for ..." in Phase 4 (A9). Fulfiller-only — Employee actions (`svc_emp_intelligence__*`) are not tracked by this skill.
111
+
112
+ ### Input
113
+
114
+ Positional arguments:
115
+
116
+ 1. `generate-prompt-response.json` — capture of `sf api request rest "/services/data/v67.0/actions/custom/generatePromptResponse" --method GET`.
117
+ 2. `expectedActions-csv` (optional) — comma-separated list of specific action names to check by exact match (e.g. `svc_itsm_intelligence__DraftMobileTaskSummary,svc_itsm_intelligence__FindContextualKA`). Callers upstream (Fulfiller agent-configure Phase 1b) hold this list; when invoked standalone, omit the CSV.
118
+
119
+ ### Output
120
+
121
+ ```json
122
+ {
123
+ "present": ["svc_itsm_intelligence__ActionA"],
124
+ "missing": ["svc_itsm_intelligence__ActionB"],
125
+ "totalItsmActionsSeen": 7,
126
+ "verdict": "PARTIAL",
127
+ "reasons": ["..."]
128
+ }
129
+ ```
130
+
131
+ Verdict values:
132
+
133
+ | verdict | Meaning | Caller action |
134
+ |---|---|---|
135
+ | `SURFACED` | With CSV: every expected action found. Without CSV: at least one `svc_itsm_intelligence__` action is present in the response | Proceed to Phase 5 → report `ASSIGNED` / `ALREADY-ASSIGNED` |
136
+ | `PARTIAL` | With CSV only: some expected actions found, others missing | Report `VERIFY-INCONCLUSIVE` — session refresh may resolve, or the wrong persona permset was assigned |
137
+ | `MISSING` | With CSV: none of the expected actions found. Without CSV: no `svc_itsm_intelligence__` actions in the response | Report `VERIFY-INCONCLUSIVE` — do not falsely report `ASSIGNED` |
138
+ | `CANNOT-CONFIRM` | Envelope missing/failed or `actions[]` array not found | Surface `reasons[]` verbatim and stop |
139
+
140
+ ## Why deterministic classifiers?
141
+
142
+ All four classifiers replace prose interpretation of query / response bodies with fixed rules that always produce the same output for the same input. This is the A9 rule the sibling agent-configure skills follow: any decision that gates a write (or a success report) on the state of a JSON body lives in a script, never in prose. Model interpretation of `records[].length === 0` is a well-documented source of false-positive skips ("classifier said empty, so no permset exists — proceed") when the real cause was an auth-failure that returned an empty envelope.
@@ -0,0 +1,82 @@
1
+ # ITSM Fulfiller Persona Permset Topology (Core-shipped)
2
+
3
+ This skill assigns Core-shipped **StandardPermissionSet** persona permsets — NOT a
4
+ managed-package namespaced permset. On a real org there is no
5
+ `svc_itsm_intelligence` namespaced `PermissionSet`; the Fulfiller agent's
6
+ `svc_itsm_intelligence__*` prompt-template invocable actions surface only once
7
+ the running user holds the correct **Fulfiller persona permset** shipped in
8
+ Core (namespace `force`).
9
+
10
+ **Employee agent is out of scope for this skill.** The Employee agent's access
11
+ model is separate (org-preferences + a different persona layer) and does not
12
+ map onto these four permsets.
13
+
14
+ ## Fulfiller persona ↔ AddOn ↔ PSL ↔ PermSet
15
+
16
+ | Persona | AddOn (`.add.xml`) | PSL (`.psl.xml`) | **PermSet DeveloperName** (assign this) | Grants userPerm |
17
+ |---|---|---|---|---|
18
+ | Incident Fulfiller | `IncidentManagementAddOn-1` | `IncidentFulfillerPsl-1` | **`IncidentFulfiller`** | `IncidentFulfillerUser` |
19
+ | Problem Fulfiller | `ProblemManagementAddOn-1` | `ProblemFulfillerPsl-1` | **`ProblemFulfillerPermSet`** | `ProblemFulfillerUser` |
20
+ | Change Fulfiller | `ChangeManagementAddOn-1` | `ChangeRequestFulfillerPsl-1` | **`ChangeRequestFulfillerPermSet`** | `ChangeFulfillerPerm` |
21
+ | Release Manager | `ReleaseManagementAddOn-1` | `ReleaseManagerPsl-1` | **`ReleaseManagerPermSet`** | (org-perm-gated only) |
22
+
23
+ ### Key facts
24
+
25
+ - **Namespace is `force` on every permset** — the namespace-prefix filter used
26
+ in the previous version of this skill (`svc_itsm_intelligence`) never
27
+ returns any of these rows.
28
+ - **Naming inconsistency**: `IncidentFulfiller` has NO `PermSet` suffix; the
29
+ other three DO. The classifier's fixed-name list reflects this exactly —
30
+ do not "normalize" the Incident name to `IncidentFulfillerPermSet`, it does
31
+ not exist on the org.
32
+ - **`ReleaseManagerPermSet`** ships as a permset but its Fulfiller user permission
33
+ lives on the OrgPreference layer (org-perm-gated). Assigning the permset
34
+ still works and is the correct write; the runtime effect activates once the
35
+ org toggle is on. Downstream, `service-itsm-agentic-setup-agentforce-studio-validate`
36
+ is the skill that toggles the org preference.
37
+ - **CMDB access is a separate concern** — the CMDB read/write permsets are
38
+ handled by `service-itsm-agentic-setup-cmdb-access-assign` and are not
39
+ gated by any `svc_itsm_intelligence__*` prompt-template invocable, so they
40
+ are intentionally excluded from this skill's fixed-lookup set.
41
+
42
+ ## Fixed-lookup discovery query (Phase 1)
43
+
44
+ ```sql
45
+ SELECT Id, Name, Label, LicenseId
46
+ FROM PermissionSet
47
+ WHERE Name IN (
48
+ 'IncidentFulfiller',
49
+ 'ProblemFulfillerPermSet',
50
+ 'ChangeRequestFulfillerPermSet',
51
+ 'ReleaseManagerPermSet'
52
+ )
53
+ ```
54
+
55
+ **Interpreting the result set:**
56
+
57
+ - **≥1 row present** ⇒ Branch A (assign). Ask the user which persona to
58
+ assign — do NOT auto-select, since a Fulfiller commonly needs only one
59
+ persona (e.g. Incident) even when others are provisioned.
60
+ - **Zero rows** ⇒ Branch B (hand-off). The ITSM AddOn(s) are not provisioned
61
+ on this org. Route to `service-itsm-agentic-setup-agentforce-studio-validate`
62
+ so the AddOn(s) can be enabled first.
63
+
64
+ ## `needsPsl` per persona
65
+
66
+ Whether the PSL POST step in Phase 2 runs is a **per-permset** decision, read
67
+ from each row's `LicenseId`:
68
+
69
+ - `LicenseId != null` ⇒ `needsPsl:true` (license-gated permset — POST the PSL
70
+ before the permset assign).
71
+ - `LicenseId == null` ⇒ `needsPsl:false` (standalone permset — skip the PSL
72
+ POST entirely; assign the permset only).
73
+
74
+ The classifier emits `needsPsl` per candidate row, so the caller reads the
75
+ switch from the selected persona and does not need to re-derive it.
76
+
77
+ ## Codesearch sources
78
+
79
+ Every row above was verified against the Core repo. When the AddOn / PSL /
80
+ PermSet metadata drifts on a future release, re-run codesearch on the AddOn
81
+ file (`*.add.xml`) — the AddOn's `<permissionSet>` reference is the source of
82
+ truth for the DeveloperName this skill must assign.