.skills/discourse-acl-authoring/SKILL.md
Use this skill before adding or reviewing ACL-backed resource permissions in Discourse core or plugins.
view, edit, and manage, but confirm the target's domain semantics.AccessControlList, AclTarget, Acl::Target, Acl::User, Guardian, User#permission_acl, or target visibility queries.DAccessControl, DAccessControlField, or FormKit ACL validation..skills/discourse-service-authoring for Service::Base changes, .skills/discourse-writing-rspec-tests for RSpec, and .skills/discourse-writing-js-tests for QUnit.AccessControlListManager.call; the manager is a destructive replacement service and currently assumes the caller already authorized the actor.AccessControlListManager for writes, including empty submitted ACL arrays, so mandatory ACLs are injected and old rows are replaced intentionally.AccessControlList.flattened_list as the API shape for UI/client payloads and AccessControlList.expand_list_for_bulk_insert as the DB insert shape.AclTarget visibility scopes for checks and target scopes instead of hand-querying ACL tables in controllers.DiscoursePluginRegistry.register_acl_target_class so Site#access_control exposes mandatory and banned ACL metadata to the frontend.banned_acl as a server-enforced restriction. DAccessControl hides banned permission choices for UX, but AccessControlListManager must still reject submitted banned entries.loss_warning_permissions as permission strings whose loss by the current actor requires confirmation. Provide the target-specific server translation used by AccessControlList::EvaluateModification.DAccessControlField inside FormKit. It owns required-permission validation, evaluates the proposed ACL, and uses FormKit's preventSubmit to cancel a submission without manufacturing a validation error.acl_target_key when using DAccessControlField; its @aclTarget.type currently serves as both the Ruby class identifier and frontend metadata key.allowed_user_ids in expansion, flattening, preloading, lookup helpers, matching scopes, and cleanup jobs, but DAccessControl remains group-first and does not yet provide complete user ACL editing.app/models/access_control_list.rbapp/models/concerns/acl_target.rblib/acl/target.rb, lib/acl/user.rblib/guardian.rbapp/models/user.rbapp/services/access_control_list_manager.rbapp/services/access_control_list/evaluate_modification.rb, app/controllers/access_control_lists_controller.rbapp/jobs/regular/cleanup_acls_for_deleted.rb, app/models/group.rb, app/models/user.rbapp/models/site.rb, app/serializers/site_serializer.rbfrontend/discourse/app/ui-kit/d-access-control.gjs, frontend/discourse/app/ui-kit/d-access-control-field.gjsspec/models/access_control_list_spec.rb, spec/models/concerns/acl_target_spec.rb, spec/lib/acl/target_spec.rb, spec/jobs/regular/cleanup_acls_for_deleted_spec.rb, spec/services/access_control_list_manager_spec.rb, spec/services/access_control_list/evaluate_modification_spec.rb, spec/serializers/site_serializer_spec.rb, frontend/discourse/tests/integration/components/d-access-control-test.gjs, frontend/discourse/tests/integration/components/form-kit/field-test.gjsplugins/discourse-kanban or the external discourse-kanban checkout when present.