---
name: rls-check
description: |
  Verify Row-Level Security on a new or modified table in db/schema.sql.
  Use when adding a new table, adding/removing tenant_id column, or modifying
  RLS policies. Walks through 7-step checklist (tenant_id, ENABLE RLS, 2+ policies,
  5-role GRANTs, db/CHANGELOG_schema.md entry, squawk, smoke test).
  For reviewing a diff, branch, or PR with DB changes - use the rls-reviewer agent.
disable-model-invocation: true
---

# RLS-check — verify RLS на таблице

## Когда использовать

При добавлении или модификации таблицы в `db/schema.sql`, особенно перед коммитом. Инкапсулирует 7-item checklist; lefthook pre-commit job 7 (squawk) ловит только часть.

Invoke via `/rls-check <table_name>`.

## Граница с агентом rls-reviewer

`rls-check` (этот скил) и `rls-reviewer` (агент, `.claude/agents/rls-reviewer.md`)
оба проверяют RLS, но в разных ситуациях. Правило выбора:

- Знаешь имя одной конкретной таблицы, проверка вручную перед коммитом →
  **`/rls-check <table>`** (этот скил).
- Есть diff / ветка / PR с изменениями БД, набор таблиц заранее не известен →
  **агент `rls-reviewer`**.

Скил работает в основном контексте по одной названной таблице и прогоняет
**8 строк вывода** — 7 статических пунктов + живой дымовой тест
(`pest --filter RlsSmokeTest`, шаг 7). Агент работает в отдельном контексте
субагента, разбирает diff/миграцию/PR и прогоняет только **7 статических**
строк — дымовой тест намеренно не запускает.

Первые 7 строк вывода у обоих — общее статическое ядро (tenant_id, ENABLE RLS,
SELECT/ALL политики, GRANT'ы 5 ролей, CHANGELOG, squawk). Это не дублирование:
ядро проверок одно, сценарии вызова разные. Дымовой тест — только в скиле:
запуск Pest в ревью-субагенте медленный и задевает гонки `--parallel`
(квирки 72/77, см. `.claude/agents/pest-parallel-debugger.md`).

## Checklist

1. **tenant_id column.** Grep `db/schema.sql` для `CREATE TABLE <name>`. Verify:
   - `tenant_id UUID NOT NULL REFERENCES tenants(id)` присутствует, **OR**
   - SaaS-level exemption — explicit comment типа `-- SaaS-level: no tenant_id (justification)`.

   ```bash
   grep -A30 "CREATE TABLE.*\b<name>\b" db/schema.sql | grep -E "tenant_id|SaaS-level"
   ```

2. **ENABLE RLS.** Должна быть строка `ALTER TABLE <name> ENABLE ROW LEVEL SECURITY;`.

   ```bash
   grep -E "ALTER TABLE\s+<name>\s+ENABLE ROW LEVEL SECURITY" db/schema.sql
   ```

3. **Policies — минимум 2.**
   - SELECT для `crm_app_user`/`crm_app_admin` с tenant scope: `USING (tenant_id = current_setting('app.current_tenant_id')::uuid)`.
   - ALL для `crm_app_admin` (или per-table convention).
   - SaaS-level: BYPASSRLS role pattern (e.g., `crm_supplier_worker`).

   ```bash
   grep -B1 -A5 "ON <name>" db/schema.sql | grep "POLICY"
   ```

4. **Role GRANTs.** В `db/02_grants.sql` должны быть GRANT'ы для 5 ролей. Проверить по pattern existing tables.

   ```bash
   grep -E "GRANT.*ON\s+<name>" db/02_grants.sql
   ```

   Expected: ≥5 GRANT statements (по одному на роль) или group GRANT.

5. **CHANGELOG entry.** В `db/CHANGELOG_schema.md` должна быть запись с датой + table name + summary (CLAUDE.md §5 п.8).

   ```bash
   grep "<name>" db/CHANGELOG_schema.md
   ```

6. **squawk lint.**

   ```bash
   ./bin/squawk.exe db/schema.sql 2>&1 | tail -10
   ```

   Expected: exit 0, no issues.

7. **Smoke test.** `tests/Feature/RlsSmokeTest.php` (или новый тест для конкретной таблицы) должен assert'ить, что user в tenant A не видит row из tenant B для новой таблицы.

   ```bash
   cd app && ./vendor/bin/pest --filter RlsSmokeTest 2>&1 | tail -10
   ```

   Expected: all assertions pass.

## Output

Print результат per item + total:

```text
RLS-check: <table_name>
  [✅] tenant_id column
  [✅] ENABLE RLS
  [✅] SELECT policy
  [✅] ALL policy
  [✅] 5-role GRANTs
  [✅] CHANGELOG entry
  [✅] squawk passes
  [✅] smoke test passes
Pass: 8/8
```

Or failure listing: `[❌] tenant_id column missing — db/schema.sql:NNNN`.

## Не использовать когда

- Modifying existing well-RLS'd table без новых columns — overhead.
- Tables explicitly outside RLS (e.g., Laravel `migrations`, `cache` — internal).
- Проверяешь не одну названную таблицу, а diff/ветку/PR с изменениями БД —
  это сценарий агента `rls-reviewer`, не скила.
