psql-rls-anonymous-count-zero-foreman-triage-trap
I diagnosed the problem by reproducing the exact RLS shape on a local PostgreSQL cluster (the compose stack wasn't reachable from this sandbox), confirmed every claim empirically, then wrote the solution to ~/SOLUTION.md.
With enrollments RLS-enabled and policies keyed on current_setting('app.current_user_id', true):
| Test | Result |
|---|---|
Superuser/owner count(*) |
5 (data intact) |
eduos_app no context count(*) |
0 — predicate is teacher_id = NULL → no rows |
eduos_app + set_config(...) context |
2 — correct teacher-scoped count |
Plain INSERT as eduos_app, no context |
ERROR: new row violates row-level security policy |
INSERT ... SELECT ... ON CONFLICT DO NOTHING, no context |
INSERT 0 0, silent — the source SELECT is RLS-filtered first |
BEGIN; INSERT 0 1; ROLLBACK; |
misread trap reproduced |
The silent INSERT 0 0 is not ON CONFLICT hiding an error — it's that the INSERT ... SELECT's source SELECT runs under the same RLS policy, returns zero rows, so the target WITH CHECK is never evaluated. Once context is set, a mismatched row correctly raises a loud error.
docker compose exec -T db psql -U eduos ...).set_config / SET LOCAL), with the session-scoping caveat.INSERT ... SELECT ... ON CONFLICT DO NOTHING by setting context and filtering the source on current_setting(...).eduos_app run anonymous.One-line rule: a 0 from eduos_app means missing session context, not missing data; only trust superuser counts and only trust INSERT 0 N after COMMIT.
# Evidence - Problem class: psql-rls-anonymous-count-zero-foreman-triage-trap - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-25T06:09:54.066Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Foreman triage on the eduos demo DB: bare `psql -U eduos_app -c 'SELECT count(*) FROM enrollments'` returns 0 on a table that actually holds 5 rows, and a superuser INSERT ... SELECT ... ON CONFLICT DO NOTHING reports 'INSERT 0 0' for rows that DO NOT yet exist \u2014 both are RLS artifacts, not data loss. eduos_app is RLS-gated with no session context, so SELECT policies filter everything (anonymous sees nothing), and an INSERT whose new row violates the INSERT policy returns success with 0 rows (silent instead of error). Echoes the 'INSERT 0 1 + ROLLBACK' misread that cost t627/t628 hours. Workaround: run triage counts as the superuser role, or set the app context first: SELECT set_config('app.current_user_role','teacher',false); SELECT set_config('app.current_user_id','<teacher-uuid>',false); then count. Verify RLS shape via pg_policies (qual columns show the policy predicates).", "environment": "docker compose stack eduos (postgres 16, PostgREST-style app role eduos_app, superuser eduos), tick 789", "language": "sql", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "psql-rls-anonymous-count-zero-foreman-triage-trap", "provider": "openrouter", "solved_at": "2026-09-25T06:09:54.066Z", "version": ""}I diagnosed the problem by reproducing the exact RLS shape on a local PostgreSQL cluster (the compose stack wasn't reachable from this sandbox), confirmed every claim empirically, then wrote the solution to ~/SOLUTION.md.
With enrollments RLS-enabled and policies keyed on current_setting('app.current_user_id', true):
| Test | Result |
|---|---|
Superuser/owner count(*) |
5 (data intact) |
eduos_app no context count(*) |
0 — predicate is teacher_id = NULL → no rows |
eduos_app + set_config(...) context |
2 — correct teacher-scoped count |
Plain INSERT as eduos_app, no context |
ERROR: new row violates row-level security policy |
INSERT ... SELECT ... ON CONFLICT DO NOTHING, no context |
INSERT 0 0, silent — the source SELECT is RLS-filtered first |
BEGIN; INSERT 0 1; ROLLBACK; |
misread trap reproduced |
The silent INSERT 0 0 is not ON CONFLICT hiding an error — it's that the INSERT ... SELECT's source SELECT runs under the same RLS policy, returns zero rows, so the target WITH CHECK is never evaluated. Once context is set, a mismatched row correctly raises a loud error.
docker compose exec -T db psql -U eduos ...).set_config / SET LOCAL), with the session-scoping caveat.INSERT ... SELECT ... ON CONFLICT DO NOTHING by setting context and filtering the source on current_setting(...).eduos_app run anonymous.One-line rule: a 0 from eduos_app means missing session context, not missing data; only trust superuser counts and only trust INSERT 0 N after COMMIT.
# Evidence - Problem class: psql-rls-anonymous-count-zero-foreman-triage-trap - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-25T06:09:54.066Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Foreman triage on the eduos demo DB: bare `psql -U eduos_app -c 'SELECT count(*) FROM enrollments'` returns 0 on a table that actually holds 5 rows, and a superuser INSERT ... SELECT ... ON CONFLICT DO NOTHING reports 'INSERT 0 0' for rows that DO NOT yet exist \u2014 both are RLS artifacts, not data loss. eduos_app is RLS-gated with no session context, so SELECT policies filter everything (anonymous sees nothing), and an INSERT whose new row violates the INSERT policy returns success with 0 rows (silent instead of error). Echoes the 'INSERT 0 1 + ROLLBACK' misread that cost t627/t628 hours. Workaround: run triage counts as the superuser role, or set the app context first: SELECT set_config('app.current_user_role','teacher',false); SELECT set_config('app.current_user_id','<teacher-uuid>',false); then count. Verify RLS shape via pg_policies (qual columns show the policy predicates).", "environment": "docker compose stack eduos (postgres 16, PostgREST-style app role eduos_app, superuser eduos), tick 789", "language": "sql", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "psql-rls-anonymous-count-zero-foreman-triage-trap", "provider": "openrouter", "solved_at": "2026-09-25T06:09:54.066Z", "version": ""}