.skills/discourse-acl-authoring/references/testing-and-review.md
Use this reference when adding specs or reviewing ACL-backed features.
For target models:
AclTarget.mandatory_acl if the target defines one.anonymous_can_read? or can_write? that wrap permission_acl..with_acl_permission and .with_any_acl_permissions when index/list actions depend on the shared scopes.For Guardian methods:
granular_anonymous_and_logged_in_groups_permissions behavior when relevant.For services using AccessControlListManager:
acl persists mandatory ACLs.acl: [] persists mandatory ACLs or fails closed when no mandatory ACL exists.acl: [] replaces existing ACLs with mandatory ACLs.banned_acl fail the has_no_banned_acl policy.permission_acl cache.For cleanup behavior:
:cleanup_acls_for_deleted.Jobs::CleanupAclsForDeleted removes the group id from ACL rows.For lookup objects:
Acl::Target#permission_group_ids returns [] for missing permissions and a defensive copy for present permissions.Acl::Target#group_ids_with_any_permission replaces the older multi_permission_group_ids name.Acl::User#target_ids_with_permission and #target_ids_with_any_permissions return defensive array copies.For migration specs:
For DAccessControl consumers:
site.access_control.mandatory_acl[targetKey] appears and is locked.site.access_control.banned_acl[targetKey] removes matching permission options for the matching grantee.onChange writes updated ACL arrays into parent state.Use .skills/discourse-writing-js-tests for QUnit patterns.
Ask these questions during code review:
AccessControlListManager?@aclTarget string match the target's acl_target_key?Target.with_acl_permission, Target.with_any_acl_permissions, guardian.target_ids_with_acl_permission, or target_ids_with_any_acl_permissions instead of ad hoc SQL?DiscoursePluginRegistry.register_acl_target_class?anonymous_users, logged_in_users, or everyone can appear in ACLs?AccessControlListManager currently assumes caller-side authorization.DAccessControl injects mandatory rows for display but does not notify the parent on render.DAccessControl filters banned permission options for UX, but hidden options are not authorization. The manager policy is the enforcement point.mandatory_values.flattened_list skips stale group IDs and unknown target classes, so a missing row can disappear from serialized ACL output while cleanup or validation catches up.