DEV Community

Cover image for 731 words or 2,042? Why an aggregate's population belongs in the view, not the query string
Kynth
Kynth

Posted on

731 words or 2,042? Why an aggregate's population belongs in the view, not the query string

I maintain a corpus of agent instruction files scraped from public repos — AGENTS.md, CLAUDE.md, Cursor rules and friends — and last week I added the page answering "what actually goes in a CLAUDE.md," counted rather than recommended.

The first thing that fell out was two different answers to median length:

basis files median words share with runnable commands
every CLAUDE.md 775 2,042 34.2%
repo-root CLAUDE.md only 201 731 79.6%

Both are correct. The 574 non-root files — the per-package ones sitting in packages/api/CLAUDE.md — have a median of 2,276 words, which is the opposite of what I'd have guessed. Whatever the cause, they are a different population answering a different question, and someone asking "what should my CLAUDE.md contain" means the one at the root.

So the interesting part isn't the number. It's that the choice of population is a schema decision, and I'd been treating it as a query parameter.

Rule 1: the filter that defines the population goes in the view

The tempting design is /api/sections?root=true. Then the default is wrong, or the caller forgets, and a percentage computed over 775 files gets printed under a heading that says "a CLAUDE.md file." Nobody will ever notice, because the number looks fine.

create or replace view rs_section_stats as
with base as (
  select format, count(*)::int as total
  from rs_configs
  where hidden = false and is_root
  group by format
)
select c.format,
       t.tag                                                 as section_tag,
       count(*)::int                                         as config_count,
       b.total                                               as root_total,
       round(100.0 * count(*) / nullif(b.total, 0), 1)::real  as pct_of_root
from rs_configs c
cross join lateral unnest(c.section_tags) as t(tag)
join base b on b.format = c.format
where c.hidden = false and c.is_root
group by c.format, t.tag, b.total;
Enter fullscreen mode Exit fullscreen mode

is_root appears twice and is optional in neither place. There is no way to call this view and get the other population. The name says pct_of_root, so a column that escapes into a page carries its own basis in the label.

Rule 2: ship the denominator on every numerator row

root_total is repeated identically on all twenty rows, which is redundant on purpose. The alternative is a page that fetches counts here and the total from somewhere else — and "somewhere else" drifts. A different hidden filter, a different date window, a count over the list endpoint that hit a row cap. A percentage with the wrong denominator is the kind of wrong that looks right.

Rule 3: two bases on one page is an invitation to divide across them

I already had a stats table publishing medians over all files. Rather than reuse it, I added a second small view, rs_root_shape, computing length and heading counts on the same root-only basis. It exists purely so a reader — or a future me — cannot pair a median from one population with a share from another in adjacent paragraphs. Duplicating an aggregate is cheaper than the errata.

Rule 4: GROUP BY cannot emit a zero

The classifier has a fixed vocabulary of 20 section tags. A group-by returns only tags that some file carries, so for windsurf-rules the view returns 13 rows and for .cursorrules it returns 19. Render that as "the tags" and you've silently claimed the missing 7 aren't categories at all — when "no file in this corpus has a security section" is one of the more interesting things you could say. The endpoint returns sections, plus vocabulary: every tag the classifier can assign. Absence is an answer, and it has to be carried out-of-band because SQL won't produce it.

Why this is a view and not application code

section_tags is a text[]. Getting a share out of it needs an unnest and a group-by — the one aggregate a list endpoint structurally can't serve. /api/configs?tag=build returns the files with a build section, never the fraction. Computing it in the page meant paging a whole format at the handler's 100-row ceiling: eight sequential reads of thirty-eight columns to produce twenty integers. One view, one read.

For the record, the finding that motivated the page: build commands come first in Anthropic's list of what to keep in a CLAUDE.md, and 39.8% of root files have a build section. Hard prohibitions are the cheapest line anyone can write, and 48.3% have those.

This is how we built RuleStack, a measured comparison hub for agent config formats: https://rulestack.kynth.studio

Top comments (0)