policy-test
Skill2 starsChanged 35 days agoMITView on GitHub
Write or review the RSpec spec for one Pundit policy so every role × action decision, every relationship rule (assigned therapist, clinical supervisor, portal actor), and every policy scope's inclusions AND exclusions are asserted, using the real roles from Role::ROLES and no redundant cross-org or delegation padding. Use when the user asks for policy tests, points at app/policies or spec/policies, asks "who can do X" to be locked down in tests, or wants a policy spec trimmed or checked.
In this repository75 other files it gives its agents.
- acceptance-criteriaSkill
- architect-guideSkill
- architect-reviewSkill
- audit-fixerSkill
- avo-admin-expertSkill
- backend-services-expertSkill
- bug-hunt-allSkill
- bug-hunt-fixSkill
- bug-huntSkill
- build-feature-remoteSkill
- build-featureSkill
- codeSkill
- component-testSkill
- controller-expertSkill
- create-qa-documentSkill
- debugSkill
- devops-expertSkill
- execute-qaSkill
- feature-qaSkill
- fix-bug-indexSkill
- fix-honeybadgerSkill
- fix-spec-bugsSkill
- fix-system-testSkill
- frontend-expertSkill
- frontend-reviewSkill
- full-qa-fixSkill
- full-qaSkill
- full-reviewSkill
- green-ciSkill
- group-issuesSkill
- honeybadger-auditSkill
- ideateSkill
- impact-assessmentSkill
- improveSkill
- merge-mainSkill
- model-expertSkill
- model-testSkill
- native-bug-hunt-fixSkill
- native-bug-huntSkill
- page-speedSkill
- plan-questionsSkill
- planSkill
- plan-system-testsSkill
- prdSkill
- prd-worktreeSkill
- product-questionSkill
- product-question-tldrSkill
- qa-branchSkill
- qa-testerSkill
- refactor-sweepSkill
- resolve-honeybadgerSkill
- resolve-issueSkill
- resolve-issuesSkill
- review-acceptance-criteriaSkill
- review-fixesSkill
- review-railsSkill
- rspec-test-expertSkill
- scale-reviewSkill
- service-testSkill
- settle-pr-reviewSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 3 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 9
Write or review the spec for the policy at $ARGUMENTS.
- Distinctive: line 24
Test a delegating action only when this policy overrides it.
- Distinctive: line 34
Read every predicate and build the grid from the code: for each action, which actors are allowed and which are denied.
- Distinctive: line 69
every role the code allows, plus the near-miss actors it must deny.
- Very specific: line 77
When a role is irrelevant to the policy (it never appears in any predicate and falls through to the default deny), one denial example for …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Policy Test
Write or review the spec for the policy at $ARGUMENTS. First read
.claude/skills/rspec-test-expert/test-quality.md (the fat catalogue, the proof, concurrency-safe
spec runs, and the review workflow). Policies are the security boundary of a HIPAA app, so here a
missing denial is worse than a missing permission.
Map paths: app/policies/client_portal/foo_policy.rb ↔ spec/policies/client_portal/foo_policy_spec.rb.
Facts that decide the setup
- Roles are
Role::ROLES:admin coordinator therapist manager clinical_supervisor biller platform_admin. There is nosupervisorand noownerrole (InsurancePolicyPolicy#owner?and the factory's:ownertrait are dead code). - Roles are added in
after(:create). Usecreate(:user, :biller);buildhas no roles. A barecreate(:user)is a therapist. For "no role" usecreate(:user, role_names: []). ApplicationPolicydefinesnew?→create?andedit?→update?and the role predicates. Test a delegating action only when this policy overrides it.- Tenant isolation is
acts_as_tenant's job (CLAUDE.md, Multi-tenancy). Don't write "admin from another org is denied" examples, and don't add org-equality checks to make them pass. - Many rules hinge on relationships, not roles:
client_therapistassignment, clinical supervision of the treating therapist, the client-portal actor (client or guardian),platform_adminon the admin subdomain. Read each predicate and find which records make it true.
Shape: a permission grid per action, then the scope
Read every predicate and build the grid from the code: for each action, which actors are allowed and which are denied. Then express it compactly. Pundit predicate matchers keep each cell to one line:
RSpec.describe SuperbillPolicy, type: :policy do
subject(:policy) { described_class.new(user, superbill) }
let(:client) { create(:client) }
let(:superbill) { create(:superbill, client:) }
context "as admin" do
let(:user) { create(:user, :admin) }
it { is_expected.to be_show }
it { is_expected.to be_destroy }
end
context "as the client's assigned therapist" do
let(:user) { create(:user, :therapist).tap { create(:client_therapist, user: _1, client:) } }
it { is_expected.to be_show }
it { is_expected.not_to be_destroy }
end
context "as a therapist NOT assigned to the client" do
let(:user) { create(:user, :therapist) }
it { is_expected.not_to be_show }
end
end(There is no permit_actions matcher in this repo. Use be_<action> / not_to be_<action>
predicates as spec/policies/superbill_policy_spec.rb does. That file is a good reference.)
For every action, include:
- every role the code allows, plus the near-miss actors it must deny. The interesting denials are the almost-allowed ones: an unassigned therapist, a supervisor of a different therapist, a biller on a clinical record, a coordinator on a destroy;
- each relationship branch: assigned vs. not, supervising vs. not, the portal actor vs. another client;
- record-state branches the policy reads (signed or locked note, soft-deleted record,
draft?): both sides.
When a role is irrelevant to the policy (it never appears in any predicate and falls through to
the default deny), one denial example for that role is enough. Don't build an exhaustive 7-role
matrix of falses.
Scopes
Pundit.policy_scope!(user, Model) (or described_class::Scope.new(user, Model.all).resolve) for
each role branch in resolve. Every scope example must assert both what is included and
what is excluded. A scope test that only checks include(record) passes for scope.all, which is
exactly the bug that leaks PHI.
it "shows a therapist only their assigned clients", :aggregate_failures do
mine = create(:client).tap { create(:client_therapist, user:, client: _1) }
theirs = create(:client)
expect(Pundit.policy_scope!(user, Client)).to contain_exactly(mine)
expect(Pundit.policy_scope!(user, Client)).not_to include(theirs) # explicit, for the reader
endPolicy-specific fat
- Cross-organization examples and
ActsAsTenant.without_tenantorg setups (tenancy is enforced elsewhere). - Seven-role matrices where five rows are the same default denial.
new?/edit?re-tested when not overridden;expect(policy.new?).to eq(policy.create?).- Nil-user examples, unless the policy explicitly handles nil (client-portal policies sometimes do). Controllers authenticate first.
superclasschecks, and roles that don't exist (:supervisor,:owner).create(:organization)plus re-settingcurrent_tenantin every file; use the default tenant.
Prove it, then report
Green, then coverage --baseline with no lost_lines. Probe the riskiest predicates, all KILLED:
- drop the relationship check (
assigned_client?(…)→true); - widen a role list (
has_any_role?(:admin)→has_any_role?(:admin, :therapist)); - in
Scope#resolve, replace the filtered relation withscope.all.
Lint with bin/standardrb <spec> and report per test-quality.md or the caller's format.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

