<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://unitautogen.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://unitautogen.com/" rel="alternate" type="text/html" /><updated>2026-10-30T06:43:57+00:00</updated><id>https://unitautogen.com/feed.xml</id><title type="html">UnitAutogen</title><subtitle>UnitAutogen auto-generates database unit tests, and the data that drives them, so the logic inside your database is actually tested.</subtitle><author><name>Munaf Khatri</name></author><entry><title type="html">Supabase is one part of HIPAA compliance</title><link href="https://unitautogen.com/blog/hipaa-evidence-for-supabase.html" rel="alternate" type="text/html" title="Supabase is one part of HIPAA compliance" /><published>2026-10-29T12:00:00+00:00</published><updated>2026-10-29T12:00:00+00:00</updated><id>https://unitautogen.com/blog/hipaa-evidence-for-supabase</id><content type="html" xml:base="https://unitautogen.com/blog/hipaa-evidence-for-supabase.html"><![CDATA[<p>Storing PHI on Supabase requires a signed BAA and a project set to High Compliance. Those cover the platform. They do not cover the RLS policies, functions or audit tables inside the project, and a HIPAA review will ask about those as well.</p>

  <p>Supabase's <a href="https://supabase.com/docs/guides/security/shared-responsibility-model">shared responsibility model</a> says the same. It asks customers to "always apply Row Level Security (RLS)" and leaves the access rules on sensitive tables to them.</p>

  <h2>What Supabase handles</h2>
  <p>On the hosted platform, Supabase encrypts data at rest and in transit, signs the BAA and checks project settings. Its HIPAA guide asks for Point in Time Recovery, SSL enforcement, network restrictions and Postgres connection logging to be turned on, and for MFA on every account with access to the project. The Security Advisor raises a warning if any of these is switched off.</p>
  <p>The advisor and the database linter also flag some risky patterns, such as a table in <code>public</code> with RLS turned off. Neither of them runs the policies, so neither can say whether a nurse at one clinic is able to read visits from another, or whether a deleted record left a trace in the audit table.</p>

  <h2>What stays with the project owner</h2>
  <p>A HIPAA review asks three questions about the database.</p>
  <ul>
    <li>Who can reach which patient rows?</li>
    <li>Is every change to that data recorded?</li>
    <li>Can anyone alter that record afterwards?</li>
  </ul>
  <p>These map onto the Security Rule. Access control falls under 164.312(a)(1), which requires that access go "only to those persons or software programs that have been granted access rights". Audit controls fall under 164.312(b). Integrity, under 164.312(c)(1), concerns protecting PHI "from improper alteration or destruction". 164.312(d) covers authentication, and 164.308(a)(8) requires all of this to be evaluated periodically and whenever the environment changes.</p>
  <p>On Supabase, almost all of this lives in SQL written by the project team. A screenshot of the policies cannot show what the database actually does when <code>authenticated</code> calls one of those functions.</p>

  <h2>Collecting the evidence from a Supabase project</h2>
  <p>UnitAutogen covers this part with the <code>unitautogen hipaa</code> command, run from the project folder while the local stack is up.</p>
<pre><code>supabase start
unitautogen --supabase hipaa --schema public</code></pre>
  <p>It connects to the local database reported by <code>supabase status</code>. That database is built from the migrations, so no patient data is involved at any point. The tool generates the tests and the rows they need, runs them and rolls the rows back. The report is written to <code>supabase/tests/seccover/hipaa/public/</code>, and the security tests it generates go into <code>supabase/tests/seccover/security/</code>, where <code>supabase test db</code> picks them up.</p>
  <p>PHI tables are identified from the schema. Where the tool gets one wrong, the correction goes in a <code>unitautogen.toml</code> file in the project folder. Every entry taken from that file is labelled in the report, so a declaration is never read as a finding.</p>
<pre><code>[phi]
tables = ["public.patients", "public.visits"]
not    = ["public.countries"]

[audit]
tables = ["audit.change_log"]</code></pre>

  <h2>What it checks</h2>
  <p><strong>Access.</strong> Functions, views and triggers that run as their owner bypass row policies. Each one is called as two different tenants, with data present on both sides, and reported as holding or leaking, along with the table, command and role. Row policies, column grants and personal data that escapes through error messages are covered as well.</p>
  <p><strong>The audit log.</strong> For every PHI table, and for each of insert, update and delete, the report states whether the change left an audit row. Each result is recorded, conditional, cancelled by the trigger, or untested. This matters on Supabase because connection logging, which the HIPAA guide asks to keep on, records only who connected and says nothing about which rows changed.</p>
  <p><strong>Tampering.</strong> For each audit table and each client role, the report checks whether that role can edit it, erase it or forge rows in it. One way this fails is a <code>SECURITY DEFINER</code> function exposed through <code>.rpc()</code> that writes to the audit table, which lets any logged-in user call it and write whatever they like.</p>
  <p><strong>Identity.</strong> Policies rely on <code>auth.uid()</code> and <code>auth.jwt()</code>. The report checks whether a client session is able to change what those return.</p>
  <p>Everything goes into one HTML report with a JSON copy. Each safeguard is marked as evidence or partial, the gaps are ranked alongside the change that fixes each one, and the report is stamped with the database, the time, the role, the tool version and a SHA-256 of every file it drew on. If the report is edited afterwards, the hashes no longer match.</p>

  <h2>Running it on every schema change</h2>
  <p>Most Supabase changes arrive as a migration in a pull request, and Supabase already documents a GitHub Action that runs <code>supabase test db</code>. Adding the hipaa command to the same job, and keeping the dated report as a build artifact, covers the evaluation safeguard and removes the rush before each audit.</p>

  <h2>What it does not do</h2>
  <p>The report is evidence for an auditor and does not certify anything. It does not look at encryption or transmission, which Supabase handles, and it does not review logs. It also cannot judge whether a policy is the right policy. If a rule is wrong by design, the report will say the database enforces it, because it does. The risk analysis, the written policies and the BAA itself remain the responsibility of the organization.</p>

  <div class="endcard"><code>unitautogen hipaa</code> is part of the Enterprise edition of <a href="/postgresql/unitautogen/hipaa/">UnitAutogen for PostgreSQL</a>. Early-stage Supabase teams can ask about the <a href="/postgresql/pricing/">startup rate</a>, and a free trial is available for running it on a real schema first.</div>]]></content><author><name>Munaf Khatri</name></author><summary type="html"><![CDATA[Supabase signs a BAA and checks project settings. RLS policies, functions, triggers and audit tables remain the project owner's responsibility, along with showing that they work. This post covers what that means for a HIPAA review and how to collect the evidence from a Supabase project.]]></summary></entry><entry><title type="html">Your Postgres RLS is a compliance control. Is it tested?</title><link href="https://unitautogen.com/blog/rls-is-a-compliance-control.html" rel="alternate" type="text/html" title="Your Postgres RLS is a compliance control. Is it tested?" /><published>2026-07-01T12:00:00+00:00</published><updated>2026-07-01T12:00:00+00:00</updated><id>https://unitautogen.com/blog/rls-is-a-compliance-control</id><content type="html" xml:base="https://unitautogen.com/blog/rls-is-a-compliance-control.html"><![CDATA[<p>If you're building on PostgreSQL (Supabase, RDS, Neon, or your own server) and touching health data, customer records, or anything an auditor cares about, Row-Level Security isn't just a nice-to-have. It's your access-control safeguard: the mechanism that keeps each patient, tenant, or customer to their own rows. Which means the moment you're under HIPAA or SOC 2, your RLS policies aren't just code. They're <em>controls</em>, and controls get audited.</p>

  <p>Here's the uncomfortable part: most RLS ships untested. And an untested access control isn't a control. It's an assumption.</p>

  <h2>RLS is literally the safeguard the frameworks ask for</h2>
  <p>HIPAA's Security Rule requires technical access controls that allow access "only to those persons or software programs that have been granted access rights" (<a href="https://www.law.cornell.edu/cfr/text/45/164.312">45 CFR &sect;164.312(a)(1)</a>). That's RLS's job description, and RLS is native PostgreSQL, enforced in the database on every deployment. If you're on Supabase, <a href="https://supabase.com/docs/guides/security/hipaa-compliance">their HIPAA guidance</a> points to RLS as the way to guarantee each provider, clinic, or patient sees only their own data; the same mechanism does the same job on RDS, Neon, or a self-hosted cluster.</p>
  <p><a href="https://supabase.com/docs/guides/security/soc-2-compliance">SOC 2</a> says the same thing in different words. Common Criteria CC6.1 requires that logical access to protected information be restricted to authorized users. RLS is that restriction, enforced in the database, where an application bug can't route around it.</p>
  <p>So the compliance question isn't "do you use RLS?" Everyone says yes. The question is "can you show it works?"</p>

  <h2>"We use RLS" is a claim. An auditor wants evidence</h2>
  <p>This is where untested RLS quietly fails. A policy with the wrong column, an <code>OR</code> that's too generous, a <code>USING (true)</code> left in from debugging: all of these pass code review and sit in production looking fine. Nothing errors. The dashboard is green. And a different tenant can read PHI.</p>
  <p>You won't catch that by reading the policy. You catch it by <em>exercising</em> it: becoming each identity (the owner, another tenant, an anonymous visitor) and checking what each one can actually <code>SELECT</code>, <code>INSERT</code>, <code>UPDATE</code>, and <code>DELETE</code>. That's the difference between asserting a control exists and demonstrating that it enforces.</p>

  <h2>What auditable evidence looks like</h2>
  <p>Evidence for an access-control safeguard is concrete and repeatable:</p>
  <ul>
    <li>a per-identity, per-table, per-command result showing exactly who can touch which rows, including who's blocked;</li>
    <li>a test suite committed alongside your migrations, so the evidence regenerates on every change;</li>
    <li>a CI gate that turns the build red the instant a policy leaks or a table ships without RLS.</li>
  </ul>
  <p>That's not a screenshot of a settings page. That's a test run you can hand to a reviewer: dated and reproducible.</p>

  <h2>Where rlsautotest fits</h2>
  <p><a href="https://github.com/unitautogen/rlsautotest"><strong>rlsautotest</strong></a> generates exactly that evidence, without you hand-writing test SQL. Point it at a disposable copy of your database; it reads your RLS policies, generates both the tests <em>and</em> the seed data that make them mean something, probes each policy as each identity, and emits a native <a href="https://pgtap.org">pgTAP</a> suite plus a per-identity access report and a CI gate. (pgTAP ships on Supabase and loads on any other Postgres; the tool handles it for you.)</p>
  <pre><code>pip install rlsautotest
rlsautotest --db-url "$DATABASE_URL" --schema public --html rls-report.html</code></pre>
  <p>One command produces the access matrix. <code>--emit</code> writes the committable suite; wire it into CI and every pull request re-proves your access controls.</p>

  <h2>The honest boundary</h2>
  <div class="endcard"><strong>What it verifies, and what stays yours.</strong> rlsautotest verifies one thing well: that your database <em>enforces what your policies declare</em>. That's the access-control safeguard: a real, checkable piece of your HIPAA and SOC 2 posture. It is not your whole compliance program. Your BAA (with Supabase, your cloud provider, or however you host), encryption, audit logging, and the rest remain yours to own. And it proves declared-versus-enforced, not intent: a policy that's wrong on purpose will be faithfully, greenly confirmed.</div>
  <p>But within its lane, it turns "we use RLS" into "here's the test run that shows our RLS restricts access as designed." For a control that's one misplaced predicate away from a breach notification, that's the difference that matters.</p>]]></content><author><name>Munaf Khatri</name></author><summary type="html"><![CDATA[Row-Level Security is your HIPAA and SOC 2 access-control safeguard on any PostgreSQL, but only if you can show it works. Why untested RLS is a compliance gap, what auditable evidence looks like, and where rlsautotest fits.]]></summary></entry><entry><title type="html">Testing Supabase RLS locally when it depends on JWT custom claims</title><link href="https://unitautogen.com/blog/testing-rls-with-jwt-custom-claims-locally.html" rel="alternate" type="text/html" title="Testing Supabase RLS locally when it depends on JWT custom claims" /><published>2026-06-22T10:00:00+00:00</published><updated>2026-06-22T10:00:00+00:00</updated><id>https://unitautogen.com/blog/testing-rls-with-jwt-custom-claims-locally</id><content type="html" xml:base="https://unitautogen.com/blog/testing-rls-with-jwt-custom-claims-locally.html"><![CDATA[<p>If your Row-Level Security depends on custom JWT claims, say an <code>org_id</code> for multi-tenancy or a role claim for RBAC, you've probably hit this: it works perfectly on Supabase Cloud, and then your local tests come back with empty tables or "new row violates row-level security policy." The policy is fine. The problem is how the claims reach <code>auth.jwt()</code> locally.</p>

  <h2>Why it works on Cloud but not in local Studio</h2>
  <p>On a real request, the chain is: GoTrue mints a JWT (running your custom access-token hook, so the custom claims are in it) &rarr; PostgREST receives that JWT and puts the decoded payload into a Postgres setting called <code>request.jwt.claims</code> &rarr; your policy calls <code>auth.jwt()</code>, which simply reads that setting. So the custom claims are present, and RLS behaves.</p>
  <p>The local <strong>Studio Dashboard</strong> breaks that chain. When you impersonate a user in the local dashboard, it isn't tightly integrated with GoTrue the way Cloud is. It gives you a session, but the JWT it puts in front of your query doesn't carry your custom claims. So <code>auth.jwt()</code> returns a payload with the custom claims <em>empty</em>, every policy that reads them evaluates false, and you get an empty result (or a denied insert). It looks like your RLS is broken; really, the test harness just isn't feeding it the claims your app feeds it.</p>

  <h2>The fix: test at the database level</h2>
  <p>The key insight is that <code>auth.jwt()</code> is not magic; it just reads the <code>request.jwt.claims</code> setting. So you can reproduce a real request exactly by setting that yourself, in a transaction, as the right role. No GoTrue, no PostgREST, no Dashboard involved:</p>
  <pre><code>begin;

-- become the role your app uses, and inject the claims GoTrue would have minted
set local role authenticated;
select set_config('request.jwt.claims',
  '{"sub":"00000000-0000-0000-0000-0000000000aa",
    "role":"authenticated",
    "app_metadata":{"org_id":"org-a","role":"admin"}}', true);

-- auth.jwt()->'app_metadata'->>'org_id' now returns 'org-a' inside your policy
select * from documents;            -- runs under RLS as this exact identity

rollback;</code></pre>
  <p>Now the policy sees the same claims it sees in production, and you can assert what this identity should and shouldn't be able to do. Copy the exact JSON shape from a real API session's token (<code>app_metadata</code> vs <code>user_metadata</code> vs a top-level claim) so it matches what your hook actually emits.</p>

  <h2>Multi-tenancy: test a real rival tenant</h2>
  <p>For tenant isolation, the meaningful negative test isn't an outsider with no org; it's a legitimate user of a <em>different</em> org. Run the same block again with <code>org-b</code> in the claims and assert they see none of org A's rows. That's the test that catches a subtly-wrong predicate like <code>org_id IS NOT NULL</code>, which a no-org outsider would sail right through but a real second tenant would expose.</p>

  <h2>Three gotchas that cause exactly this</h2>
  <ul>
    <li><strong><code>set_config(..., true)</code> is transaction-local.</strong> It only lasts for the current transaction, so the <code>set_config</code> and the query have to be in the <em>same</em> transaction. Run them separately and the claim is already gone.</li>
    <li><strong>You must <code>set role</code>.</strong> If you stay connected as the table owner or a superuser, RLS is bypassed entirely, so everything passes and proves nothing. Become <code>authenticated</code> or <code>anon</code>.</li>
    <li><strong>The JSON shape must match.</strong> If your policy reads <code>auth.jwt()-&gt;'app_metadata'-&gt;&gt;'org_id'</code>, the claims you set need that nesting exactly.</li>
  </ul>

  <h2>The bonus: this runs in CI</h2>
  <p>The Dashboard will never run in your pipeline, but this does. Wrap the same pattern in <a href="https://pgtap.org">pgTAP</a> and you have RLS tests, including the custom-claim and multi-tenant cases, that run on every push, the same way the app actually executes them.</p>

  <div class="endcard">Writing one of these by hand per table, per command, per identity is a lot of careful SQL. <a href="https://github.com/unitautogen/rlsautotest"><strong>rlsautotest</strong></a> generates it for you: it reads your policies, sets the claims per identity and tenant, seeds matching data, and asserts both directions, entirely at the DB level so it's immune to the Studio/GoTrue gap. Free and open source.</div>

  <p>However you get there, the principle is the same: test RLS by setting the claims yourself at the database level, as the role your app uses. That's the only place the policy runs the way production runs it.</p>]]></content><author><name>Munaf Khatri</name></author><summary type="html"><![CDATA[Why RLS that reads JWT custom claims passes on Cloud but fails in local Supabase Studio, and how to test it correctly at the database level, independent of GoTrue and the Dashboard.]]></summary></entry><entry><title type="html">Most Postgres RLS ships untested. Here’s how to test it with pgTAP.</title><link href="https://unitautogen.com/blog/most-postgres-rls-ships-untested.html" rel="alternate" type="text/html" title="Most Postgres RLS ships untested. Here’s how to test it with pgTAP." /><published>2026-06-22T09:00:00+00:00</published><updated>2026-06-22T09:00:00+00:00</updated><id>https://unitautogen.com/blog/most-postgres-rls-ships-untested</id><content type="html" xml:base="https://unitautogen.com/blog/most-postgres-rls-ships-untested.html"><![CDATA[<p>Row-Level Security is the security boundary of most Postgres and Supabase apps. It's the one thing standing between "users see their own data" and a cross-tenant leak. And yet most RLS I see in the wild has zero tests, not because people don't care, but because testing RLS well is fiddly, and a test that <em>looks</em> like it passes often proves nothing at all.</p>

  <p>This post is about how to actually test RLS with <a href="https://pgtap.org">pgTAP</a>: the mechanics, the traps, and the handful of cases that bite you. No tooling required to follow along, just <code>psql</code> and pgTAP (which Supabase ships, and you can <code>CREATE EXTENSION pgtap</code> anywhere else).</p>

  <h2>Why RLS is hard to test</h2>
  <p>Four things make RLS testing different from normal unit testing:</p>
  <p><strong>1. You have to <em>become</em> each identity.</strong> A policy like <code>USING (owner = auth.uid())</code> behaves completely differently depending on who's asking. So a real test has to impersonate each one (anonymous, authenticated user A, authenticated user B, the service role) and check what each can actually do. In Postgres/Supabase terms that's <code>SET ROLE</code> plus setting the request's JWT claims.</p>
  <p><strong>2. <code>USING</code> and <code>WITH CHECK</code> are not the same thing.</strong> <code>USING</code> controls which rows you can see/affect (SELECT, UPDATE, DELETE). <code>WITH CHECK</code> controls which rows you're allowed to write (INSERT, and the new value on UPDATE). A table can be perfectly locked down for reads and wide open for writes, or vice versa. You have to test both directions.</p>
  <p><strong>3. "Denied" has two completely different shapes.</strong> When someone can't do something, it's either filtered to <strong>zero rows</strong> (RLS silently hides rows, no error) or a hard <strong>permission error</strong> (<code>42501</code>; the role doesn't even have the table grant). These mean different things, and conflating them hides bugs. A test that just checks "got an error" will miss an RLS policy that's silently returning everyone's rows, because that path doesn't error; it returns data.</p>
  <p><strong>4. The seed-data trap. This is the big one.</strong> A test only proves something if the data driving it actually exercises the policy. If your table is empty, "user B sees 0 rows" passes whether or not the policy works. If the row you seeded isn't actually owned by the user you're impersonating, "owner sees their row" fails, or worse, accidentally passes for the wrong reason. The data has to match the identity and the predicate, or the green checkmark is a lie.</p>

  <h2>A real pgTAP RLS test</h2>
  <p>Here's an owner-scoped <code>documents</code> table tested by hand. The shape is Arrange &rarr; Act &rarr; Assert, wrapped in a transaction that rolls back so tests don't pollute each other.</p>
  <pre><code>begin;
select plan(3);

-- ARRANGE: seed as the test-runner role (superuser/owner -- bypasses RLS).
-- Two users, one document owned by user A.
insert into auth.users (id) values
  ('00000000-0000-0000-0000-00000000000a'),
  ('00000000-0000-0000-0000-00000000000b');
insert into documents (owner, title)
  values ('00000000-0000-0000-0000-00000000000a', 'A''s doc');

-- ACT/ASSERT as user A (the owner) -- should see exactly their row
set local role authenticated;
select set_config('request.jwt.claims',
  '{"sub":"00000000-0000-0000-0000-00000000000a","role":"authenticated"}', true);
select is( (select count(*) from documents)::int, 1, 'owner sees their own row' );

-- as user B (a different authenticated user) -- should see nothing
select set_config('request.jwt.claims',
  '{"sub":"00000000-0000-0000-0000-00000000000b","role":"authenticated"}', true);
select is( (select count(*) from documents)::int, 0, 'another user sees nothing' );

-- as anon -- should see nothing
reset role;
set local role anon;
select set_config('request.jwt.claims', '', true);
select is( (select count(*) from documents)::int, 0, 'anon sees nothing' );

select * from finish();
rollback;</code></pre>
  <p>Notice the two things that make this <em>mean</em> something: we seeded a row that is genuinely owned by user A (so "owner sees their row" is a real assertion, not a fluke against empty data), and the negative case is a real, different authenticated user, not just "logged out."</p>

  <h2>The cases that bite you</h2>
  <p>Once the basic shape is in place, these are the ones that catch people:</p>
  <p><strong>INSERT is governed by <code>WITH CHECK</code>, which constrains the row, not the caller.</strong> A user who can't see another tenant's data can usually still insert <em>their own</em> row, and that's correct, not a hole. The actual hole is <code>WITH CHECK (true)</code> (anyone can write anything) or RLS being off entirely. So an INSERT test should assert two things: the caller can insert a row that satisfies the check, and cannot insert one that violates it (e.g. a row attributed to someone else).</p>
  <pre><code>-- a different user inserting their OWN row: allowed (WITH CHECK passes)
select lives_ok(
  $$ insert into documents (owner, title) values (auth.uid(), 'mine') $$,
  'user can insert their own row'
);
-- inserting a row owned by someone ELSE: must be rejected
select throws_ok(
  $$ insert into documents (owner, title)
       values ('00000000-0000-0000-0000-00000000000a', 'not mine') $$,
  '42501', null, 'user cannot insert a row for another owner'
);</code></pre>
  <p><strong>Tenant isolation needs a real rival tenant.</strong> The meaningful negative test for <code>org_id = (auth.jwt() -&gt; 'app_metadata' -&gt;&gt; 'org_id')::uuid</code> isn't an outsider with no org; it's a legitimate user of a <em>different</em> org. That's the test that catches a subtly-wrong predicate like <code>org_id IS NOT NULL</code>, which a no-org outsider would pass right through.</p>
  <p><strong>RBAC via SECURITY DEFINER functions.</strong> Policies that delegate to <code>authorize('documents.read')</code> or <code>has_role('admin')</code> are opaque to a black-box test; you can't always drive them through the JWT. You test the <em>wiring</em>: set up the state the function reads (or control the function), then assert that the policy is allowed when it returns true and denied when it returns false.</p>
  <p><strong>Self-referential policies.</strong> A policy that queries its own table (a recursive folder tree, say) can throw <code>infinite recursion detected in policy</code>, which locks out <em>every</em> client role. Worth an explicit "the table is even readable" test.</p>

  <h2>Doing this for a whole schema</h2>
  <p>None of the above is hard for one table. The problem is that a real app has dozens of tables, four commands each, and several identities, and every single combination needs its own seeded precondition to mean anything. That's a lot of careful, repetitive SQL, and the failure mode is silent: a test that passes against the wrong data tells you you're safe when you're not.</p>
  <div class="endcard">That repetition is what pushed me to write a generator. <a href="https://github.com/unitautogen/rlsautotest"><strong>rlsautotest</strong></a> reads your policies straight from the catalog and emits this pgTAP for you, including the seed data that matches each policy and identity. It's free and open source (Apache-2.0). But whether you generate the tests or hand-write them, the principles above are the part that matters.</div>
  <p>Become each identity, test both <code>USING</code> and <code>WITH CHECK</code>, distinguish "0 rows" from "denied," and make sure your seed data actually exercises the policy. Your RLS is your security boundary. Test it like one.</p>]]></content><author><name>Munaf Khatri</name></author><summary type="html"><![CDATA[How to actually test Postgres / Supabase Row-Level Security with pgTAP: becoming each identity, USING vs WITH CHECK, '0 rows' vs 'denied', the seed-data trap, and the cases that bite you.]]></summary></entry></feed>