本文へ移動
cccskills
無料GitHub で公開

permission-set-group-composition

Tactical guidance for composing Permission Set Groups: layering permission sets, applying Mute Permission Sets to subtract narrow capabilities, sequencing the recalculation lifecycle, deletion order, and assignment-vs-activation lifecycle. Triggers: 'PSG composition', 'mute permission set', 'PSG recalculation', 'cannot delete permission set in PSG', 'PSG explosion', 'expired PSG assignment'. NOT for the strategic profile-vs-PSG choice or migrating off profiles - use admin/permission-set-architecture. NOT for record-sharing design (OWD, role hierarchy, sharing rules) - use admin/sharing-and-visibility.

インストール方法を見る

含まれるファイル(8)

  • SKILL.md20.6 KB
  • references/examples.md11.9 KB
  • references/gotchas.md8.7 KB
  • references/llm-anti-patterns.md9.1 KB
  • references/metadata-examples.md14.0 KB
  • references/well-architected.md7.4 KB
  • scripts/check_permission_set_group_composition.py17.9 KB
  • templates/permission-set-group-composition-template.md4.5 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Permission Set Group Composition

Activate this skill when the strategic decision to use Permission Set Groups is already made and the question is now tactical: which permission sets go into which PSG, when to add a Mute Permission Set, in what order to delete a PS that is wired into 50 PSGs, why a freshly assigned PSG still does not show its effective access, and how to keep PSG count from exploding. This is the operating manual that lives one floor down from admin/permission-set-architecture — that skill picks the access model, this skill makes the model survive contact with production.

This is NOT the place to argue PSG vs profile-stack — see admin/permission-set-architecture for the strategic case. This is also NOT for record-visibility design — sharing rules, OWD, and role hierarchy are out of scope.

Before Starting

  • How many PSGs already exist, and how many of them share at least one permission set? Heavy overlap is the signal for the "PSG explosion" anti-pattern this skill prevents.
  • Is the desired delta from an existing PSG subtractive (mute) or additive (a new small PS)? Mute Permission Sets only ever subtract — they cannot grant.
  • What user licenses are attached to the target personas, and do any included PSes require a feature license that the target user does not have?
  • Is the org source-tracked (DX/Source) or change-set based? Mute Permission Sets are separate metadata files and must be retrieved explicitly — they do not travel inside the permissionsetgroup-meta.xml file.

Questions to Ask Before Configuring

Put these to the requester before opening Setup or writing the XML. Each one exists because a specific trap in references/gotchas.md (cited by number) is cheap to avoid at question time and expensive to unwind after a group is assigned to 300 users.

QuestionWhy it mattersWhat a good answer addsWhat proper configuration adds over just doing it
"Which job function is this group for, and which capabilities does that job need — named one at a time?"Gotcha 7: a group named after an incumbent or a department rather than a job function is the seed of PSG explosionA capability list that maps onto small composable permission sets, plus the job title rather than the person's nameOne group per job function that outlives the incumbent, instead of a 60-group estate that has to be excavated at audit time
"Of the permission sets this group needs, which already exist and are shared with other groups, and which would be private to this one?"Gotcha 5: a shared set cannot be deleted while any group still references it, so today's sharing decision sets tomorrow's retirement costA reuse map — which sets are org-wide building blocks, which are single-group, and who owns eachRetirement runs as a planned detach → wait → delete ladder instead of a destructive deployment that fails halfway
"Is the delta from the closest existing group subtractive or additive — and if subtractive, is anyone in this persona still allowed the permission?"Gotcha 1: muting is one-way, so no included set can hand a muted permission back, and a persona that needs it sometimes needs a second group rather than a muteA mute-vs-new-set decision with the exact object and permission namedA one-file subtractive delta instead of a cloned group that drifts from its original inside one release
"When does this access have to be live, and who is watching the group's status between deploy and go-live?"Gotcha 2: recalculation is asynchronous, so assignments made while status is Outdated look complete and grant nothingA go-live time, a named owner for the status poll, and a quiet window for edits to high-fan-out setsUsers get access at the hour they were promised it, instead of a support queue whose answer is "wait and try again"
"Does this access belong behind a session — hasActivationRequired true — and if so, what performs the activation?"Gotcha 3: hasActivationRequired (api_meta L95331, API 53.0+) is a third gate on top of assignment and recalculation, and each one can be true while the others are notA standing-access-vs-session-activated decision and the UI or API call that activates itElevated capability that only exists inside an activated session, rather than standing privilege nobody remembers granting
"Which environments carry this group, and does the name encode the environment as PSG_<persona>_<env>?"Gotcha 4: cross-environment moves are where the muting set gets dropped from a hand-curated manifest, and an environment-suffixed name is what makes that manifest reviewableThe environment list plus the manifest entries — PermissionSet, MutingPermissionSet and PermissionSetGroup named together (api_meta L95396)The same composition lands in every org, instead of a production group whose mutes silently never travelled
"What single line goes in each description, and where does the composition rationale live instead?"Gotcha 9: PermissionSet.description is capped at 255 characters (api_meta L94788), and an over-length set is rejected while every group composing it fails with permission set names are invalidA one-line label per file plus the named home for the rationale — the package template or the configuration workbookA deployment that validates first time, instead of a composition-layer error that sends the team hunting one layer above the real fault

What a proper composition adds over just building the group: the persona's access is a named union of reusable parts with its subtractions in one auditable file, the rollout waits for the platform instead of racing it, and the same shape deploys to every org because the manifest and the naming were decided before the first click.

Core Concepts

Composition Is A Union, Mute Is A One-Way Subtract

A Permission Set Group's effective access is the union of every permission granted by every included permission set, minus anything subtracted by an attached Mute Permission Set. The grant-side is symmetric (any included PS can grant a permission and the user gets it) but the mute-side is not symmetric — once a permission is muted on the PSG, no other included PS in the same PSG can grant it back. Grant-wins inside a PSG only applies between included PSes; mute always wins over included grants.

This asymmetry is the single most-misunderstood fact about PSGs. "Mute X then re-grant X via another PS in the same PSG" does not work — the user still does not get X.

The Recalculation Lifecycle Is Asynchronous

When a permission set that lives in N PSGs changes, every one of those PSGs enters a recalculation state. During recalculation the PSG status shows "Updating" and the effective access is whatever was calculated last — a freshly assigned user can wait for access while the PSG completes its recalc. Recalc time scales with PSG count, included PS count, and assignee count. Plan rollouts so PS edits land in a quiet window, not at the start of a release where downstream agents will hit stale access.

Status on the PSG metadata reports the result: Updated (good), Outdated (recalc not started), Updating (recalc running), Failed (recalc could not complete). Until status is Updated, do not assume new permissions are live.

Assignment Is Distinct From Activation

A PSG is activated by Salesforce after recalculation completes. A PSG is assigned to a user via PermissionSetAssignment (yes — the same SObject that holds permission-set assignments; PermissionSetGroupId is set instead of PermissionSetId). A user can be assigned to a PSG that is still Outdated; they will not see effective access until the PSG flips to Updated. Conversely, deactivating or deleting a PSG without first removing assignments is blocked.

Expiration On PSG Assignments

Since Spring '23 Salesforce supports ExpirationDate on Permission Set Group assignments — the same way it has supported expiration on Permission Set assignments. This is the right primitive for time-boxed elevation (contractor access, quarterly approver rights). Do not invent a Flow that "removes the PSG at midnight" — set the expiration and let the platform expire it automatically.

Deletion Order: Detach, Wait, Delete

You cannot delete a permission set that is still referenced by a PSG. The supported sequence is:

  1. Remove the PS from every PSG that includes it.
  2. Wait for every affected PSG to finish recalculation (Status = Updated).
  3. Delete the PS.

Skipping step 2 risks a delete that fails mid-deployment because the recalc had not yet released the dependency. Tooling that runs delete immediately after update on a PSG often hits this — add an explicit wait or split the deployment.

Common Patterns

Sales-Rep-With-Manager-Mute Pattern

When to use: A manager persona needs everything a sales rep has, except one or two narrow permissions (classic example: "Manager has Sales but NOT Delete on Opportunity" — managers should not bulk-delete pipeline data).

How it works: Build one PSG_SalesRep_Prod that includes the small composable PSes (PS_OpportunityRead, PS_OpportunityCreate, etc.). Build one Mute Permission Set MutePS_NoOpportunityDelete that subtracts Delete on Opportunity. Build PSG_SalesManager_Prod that includes the same sales PSes plus the mute PS. Two PSGs, one set of underlying PSes, one mute. No cloning.

Why not the alternative: Cloning PSG_SalesRep_Prod to PSG_SalesManager_Prod and editing one permission means every future change to the rep bundle has to be re-applied to the manager bundle. Drift starts the day the clone happens.

Small Composable PSes Over Mega-PSes

When to use: Whenever a PS is starting to grow past ~30 object-permission grants or covers more than one "capability."

How it works: Split into PS_<feature>_Read, PS_<feature>_Edit, PS_<feature>_Delete and let PSGs compose them. The PSG carries the persona shape; PSes carry the feature shape.

Why not the alternative: A single PS_Sales_Everything PS forces every persona that wants any of it to get all of it, which forces a mute for every exclusion, which inflates mute count and hides intent.

Time-Boxed Elevation

When to use: A user needs temporary elevated access (contractor, on-call rotation, audit support).

How it works: Assign the existing PSG with an ExpirationDate set on the PermissionSetAssignment row. Salesforce expires the assignment automatically; Setup Audit Trail records the expiration.

Why not the alternative: A custom Flow that deletes the assignment introduces a moving part that can fail silently. The platform-native expiration is auditable and does not depend on scheduled job health.

Deletion-Order Dance For Retiring A PS

When to use: A permission set is referenced by N PSGs and needs to go away.

How it works:

  1. Retrieve every PSG that lists the PS in permissionSets.
  2. Update each PSG to remove the reference (one deployment).
  3. Poll PSG Status until every affected PSG reports Updated.
  4. Deploy the PS deletion.

Why not the alternative: A single deployment that updates the PSGs and deletes the PS in the same transaction will fail because the recalc has not yet released the FK-style reference.

Decision Guidance

Use this when the request is "I need persona Y who is mostly like persona X except…":

SituationRecommended ApproachReason
Persona Y needs less than the closest PSG (a permission must NOT be granted)Add a Mute Permission Set to a new PSG variantSubtractive delta is exactly what mute is for; no PS duplication
Persona Y needs more than the closest PSG (a new capability)Build a new small composable PS and add it to the PSGMute cannot grant — additive deltas need a real PS
Persona Y needs a different combination of existing PSesBuild a new PSG referencing the existing PSesPSGs are cheap; PSes are the reusable unit
Persona Y is a one-off (single user, ≤30 days)Direct PS or PSG assignment with ExpirationDateDon't distort the architecture for a temporary need
Existing PSG is "almost right" for many personasRefactor — split the mega-PS into composable PSesA PSG that needs muting for every persona is signalling its underlying PSes are too coarse
The same PS appears in 5+ PSGsLikely fine — that is reuse workingReuse of small PSes across PSGs is the goal, not a smell
The same permission appears granted in multiple PSes inside the same PSGConsolidate the duplicates into one PSHidden duplication makes future muting harder to reason about

Recommended Workflow

  1. Inventory existing PSGs. Run python3 scripts/check_permission_set_group_composition.py --manifest-dir <path> (add --strict to fail the run on the naming convention as well) against the permissionsetgroups/ and permissionsets/ directories — capture which PSes are referenced in multiple PSGs (good — reuse), which PSGs have zero included PSes (orphan), which PSGs use mute PSes (good — explicit subtract), which names violate the convention, and any description over length (PSGC-DESC-01 ERROR at 255+ characters, PSGC-DESC-02 INFO headroom at 200+, never fails the run).
  2. Identify the closest existing PSG. Compare the target persona to existing PSGs and decide: subtractive delta (mute), additive delta (new PS), or different combination (new PSG).
  3. Apply the Decision Guidance table. Choose mute, new PS, or new PSG based on the row that matches the request. Avoid cloning; cloning is the explosion vector.
  4. Compose the PSG. Use the template at templates/permission-set-group-composition-template.md. Fill persona name, included PSes, mute PS (if any), license dependency, and lifecycle stage (draft / piloted / production). For the deployable XML — permission sets, muting set, group, package.xml, and the deploy order between them — copy from references/metadata-examples.md.
  5. Plan recalculation. If a frequently-referenced PS is being touched, list every PSG that will recalc. Schedule the change for a quiet window. Do not pair a PS edit with a PS deletion in the same deployment.
  6. Roll out with assignment-vs-activation in mind. Wait for Status = Updated before assigning users. For time-boxed elevation, set ExpirationDate on the assignment.
  7. Verify and audit. Confirm Setup Audit Trail captured the composition change, run the checker again, and update the inventory artifact.

Review Checklist

  • No PSG was cloned to make a small variant — mute PS used instead for subtractive deltas.
  • No mute-then-re-grant pattern inside the same PSG (mute always wins).
  • Naming follows PSG_<persona>_<env> and MutePS_<scope>_<delta>.
  • Every PSG Status is Updated before users are assigned to it.
  • Permission set deletion sequenced as detach → wait for recalc → delete.
  • Time-boxed assignments use ExpirationDate, not custom Flows.
  • Each included PS appears in ≥2 PSGs OR is documented as persona-specific.
  • Mute Permission Sets retrieved as separate metadata in source-tracked deployments.
  • Setup Audit Trail change reviewed for the rollout.
  • No description on a permission set, PSG, or mute set exceeds 255 characters; composition rationale lives in the template, not the metadata.

Salesforce-Specific Gotchas

  1. Mute is one-way subtract — grant inside the same PSG cannot beat it. A user with a muted permission stays muted even if another included PS grants it. Architecture diagrams that show "PS_A grants Delete, MutePS subtracts Delete, PS_B grants Delete → user gets Delete" are wrong.
  2. Recalculation is asynchronous and can fail. Until Status = Updated, assigned users do not see new effective access; if status is Failed, the cause must be diagnosed (most often a deleted PS reference or a license incompatibility).
  3. You cannot delete a PS while any PSG still references it. Salesforce returns a delete error; the fix is the detach → wait → delete sequence.
  4. Mute Permission Sets are separate metadata. A change set or package.xml retrieve that pulls only PermissionSetGroup will not bring the mutes — they require an explicit MutingPermissionSet (Metadata API type) entry.
  5. License mismatch on an included PS silently breaks the PSG for some users. A PSG that includes a PS scoped to "Salesforce" license cannot grant those permissions to a user on the "Platform" license — the PSG is valid, but the effective access for that user is reduced without warning.
  6. A description over 255 characters fails the deploy, and the PSG fails as a cascade. PermissionSet.description is capped at 255 characters; a PSG that composes a rejected set fails too, with permission set names are invalid — a composition-layer symptom of a field-length problem one layer down.

Output Artifacts

ArtifactDescription
Composition planPersona, included PSes, mute PS, license dependency, lifecycle stage (uses the template)
Recalculation rollout sequenceOrdered list of PSGs that will recalc when a referenced PS changes, with a quiet-window recommendation
Deletion planDetach → wait → delete sequence for retiring a PS that is referenced by one or more PSGs
Composition checker reportOutput of scripts/check_permission_set_group_composition.py — ERRORs on platform facts (empty PSG, duplicate PS in a group, missing label, unknown status, a description over 255 characters — PSGC-DESC-01), WARNs on naming-convention violations and unresolved references (promoted to failures by --strict), INFOs on a description over 200 characters (PSGC-DESC-02, never promoted), GOODs on multi-PSG reuse and mute usage

Related Skills

  • admin/permission-set-architecture — strategic counterpart: profile-vs-PS-vs-PSG architecture and migration off profile-centric access. Read it first if the request is "should we even be using PSGs."
  • security/permission-set-groups-and-muting — security-pillar framing for the same domain; reach for it during security review.
  • admin/permission-sets-vs-profiles — admin-level distinction between PSes and profiles; useful when the request is about the assignment basics rather than composition tactics.
  • devops/metadata-api-retrieve-deploy — when the deployment context surfaces the Mute Permission Set retrieval gotcha.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Use this skill when writing test-first, behavior-driven acceptance criteria in Given/When/Then format for a Salesforce user story. Covers happy path, edge cases, negative paths, permission boundaries, and data-state preconditions so the AC block can drive UAT scripts and Apex test design downstream. Trigger keywords: given when then, gherkin, behavior driven AC, test first acceptance criteria, scenario outline, BDD acceptance criteria. NOT for the user-story format itself (use admin/user-story-writing-for-salesforce). NOT for UAT script writing (use admin/uat-test-case-design). NOT for Apex test method generation (use agents/test-class-generator). NOT for high-level UAT planning (use admin/uat-and-acceptance-criteria). More triggers: acceptance criteria record, ac_id, req_id on a criterion, artefact under test, negative case for a rule-type requirement, no-match fall-through criterion, oracle for a Then clause, test type apex flow manual, criteria linter, check_ac_format.py, --manifest-dir.

日本語の概要は準備中です。原文の説明を表示しています。

PranavNagrecha/AwesomeSalesforceSkills192026年10月4日 更新

Task and Event objects: polymorphic WhatId/WhoId, Activity object model, ActivityHistory vs OpenActivity, activity timeline customization, bulk task creation, Einstein Activity Capture boundaries. NOT for turning on EAC or calendar sync — use admin/einstein-activity-capture-setup. NOT for Email-to-Case — use admin/case-management-setup. Covers ActivitiesSettings metadata, enableActivities, shared Task/Event custom fields and FLS, TaskStatus value sets, Shared Activities and TaskRelation/EventRelation, recurring and child activities, and partial-success bulk DML on Task.

日本語の概要は準備中です。原文の説明を表示しています。

PranavNagrecha/AwesomeSalesforceSkills192026年10月4日 更新

Design Invocable Apex actions that return deterministic, agent-friendly errors instead of surfacing raw exceptions to the LLM. NOT for authoring the action itself — @InvocableMethod schema, labels, security context — use agentforce/custom-agent-actions-apex. NOT for the Apex tests that force each error branch — use agentforce/agent-action-unit-tests.

日本語の概要は準備中です。原文の説明を表示しています。

PranavNagrecha/AwesomeSalesforceSkills192026年10月4日 更新

Use when designing how an Agentforce agent extracts structured input parameters (slots) from a user's natural-language utterance to invoke an action. Triggers: 'agent extract date from user', 'agent action argument extraction', 'agentforce slot filling', 'agent invocable input mapping', 'agent fails to fill required parameter'. NOT for action authoring (use agentforce/agent-actions) or for prompt-template variable binding (use agentforce/prompt-builder-templates).

日本語の概要は準備中です。原文の説明を表示しています。

PranavNagrecha/AwesomeSalesforceSkills192026年10月4日 更新

Apex test patterns for @InvocableMethod agent actions: per-reason-code branch coverage, bulk safety, callout mocking, deterministic assertions. NOT for testing whether the agent routes to the right topic or answers well — use agentforce/agent-testing-and-evaluation. NOT for designing the error envelope the tests assert on — use agentforce/agent-action-error-handling.

日本語の概要は準備中です。原文の説明を表示しています。

PranavNagrecha/AwesomeSalesforceSkills192026年10月4日 更新

Designing or reviewing Agentforce actions: Flow actions, Apex invocable actions, prompt-template actions, action naming, input and output contracts, confirmation requirements, and safe error behavior. Triggers: 'agent actions', 'build an agent that can look up an order', 'make the agent able to check order status', 'give the agent a capability', 'agent that can retrieve a record', 'flow action for agent', 'agent invocable action', 'action schema design'. NOT for writing the Apex class itself — use agentforce/custom-agent-actions-apex. NOT for the reason_code error envelope that stops an agent retry loop — use agentforce/agent-action-error-handling.

日本語の概要は準備中です。原文の説明を表示しています。

PranavNagrecha/AwesomeSalesforceSkills192026年10月4日 更新

PranavNagrecha のスキルをすべて見る

このスキルの問題を報告する