ABS Sort Pages: By Grade and Date, the Rules
ABS review listings default to newest-first; the secondary key is grade. URL params (?sort=grade|oldest|new) reach a /reviews/[sort].astro. The four tie-breakers and why we don't sort by score.

The convention in one sentence
ABS review listings default to newest-first; the secondary key (tie-breaker) is the grade chip (A/B/C/D/F). Three URL params — ?sort=new, ?sort=grade, ?sort=oldest — flip the primary key. We don’t sort by score.
The “4” tie-breakers in order after grade:
- Grade (A-F, A highest)
- Score within the same grade (higher first)
- Date within the same grade+score (newest first)
- Slug (deterministic; ensures idempotent order)
The URL paths: /reviews/ is the default newest-first; /reviews/grade/ is highest grade; /reviews/oldest/ is oldest-first.
Why grade-first, not score-first
The intuition would be “sort by score because that’s what readers care about.” We don’t, for “3” reasons:
- Score is a continuous signal; grade is a discrete signal that reflects editorial weight. A “91” and a “92” might both be A; sorting by score would put “92” first, hiding the editorial judgment that both are A-grade.
- Score regresses to mean. Two reviews with similar scores (“87” and “89”) might have very different editorial import. Grade converts noise into signal.
- Readers relate to grade. “I want A-grade reviews” is a useful query; “I want reviews ≥87” is not.
The “91 <-> 92” swap example: under score-first sorting the “92” review ranks above the “91”; under grade-first, both end up adjacent, and the more recent one wins.
The three URL params
| Param | Primary key | Tie-breaker | When to use |
|---|---|---|---|
| (default) | Date desc | Grade, then score | Homepage, /reviews/ listing |
?sort=grade (path /reviews/grade/) | Grade desc | Score desc, then date desc | ”Show me the best stuff first” |
?sort=oldest (path /reviews/oldest/) | Date asc | Grade, then score | Archive-style, “show history” |
A ?sort=score query param exists and is intentional — but it’s the lowest-priority param and we don’t promote it. A direct request still works for the operator or QA paths.
The Astro route
src/pages/reviews/[sort].astro handles the dynamic case. The static /reviews/index.astro handles the default. The [sort].astro validates against the “3” accepted values (new, grade, oldest), throws otherwise.
// src/pages/reviews/[sort].astro (simplified)
export async function getStaticPaths() {
return [
{ params: { sort: 'new' } },
{ params: { sort: 'grade' } },
{ params: { sort: 'oldest' } },
];
}
const sort = Astro.params.sort;
const sorted = sortReviews(sort);
---
{sorted.map(r => <ReviewCard post={r} />)}
The sortReviews() helper lives in a shared utils file and is reused by /reviews/index.astro and /category/[cat]/[sort].astro paths.
The tie-breakers, applied
When two reviews are equally graded, the sort picks by score. The score is the sum of the “4” rubric dimensions (signal, frame, evidence, fit) and ranges “0-100”. The published review carries the score in its frontmatter; the home page displays a band (good/mid/low) rather than the raw number.
When two reviews tie on score, the sort picks by date (newer first within a tie). This is the only place date acts as a primary criterion.
What we don’t do
- Sort by raw score as primary key. Grade-first prevents the regression-to-mean problem.
- Sort by author. ABS has a single editorial voice; author sort would be uniform.
- Sort by title alphabetically. Random for editorial consumption.
- Sort by tag count. A review with “0” tags and a “well-known” product would rank below a tag-heavy but irrelevant review.
- Add a sticky / pinned / featured-review state. All reviews sort uniformly.
Edge cases
“3” quirks in the data the sort handles:
- A review with no grade yet (in flight). The review still appears, sorted to the bottom (grade F is the placeholder; if the score is also missing, the date alone is the order).
- A review with a future
pubDate(scheduled). The review appears at the top of newest-first until it goes live, then settles into the queue. We don’t artificially hide scheduled reviews; we surface the schedule. - Slug collisions. The tie-breaker order has slug-last on purpose — deterministic / idempotent. Slug collisions shouldn’t happen given our authoring, but if one sneaks in, the order is reproducible.
What this saves
For the operator: no manual reordering. Adding a new review places it in the appropriate slot automatically.
For the reader: a consistent mental model. Grade-first / score-second / date-third is a tree of cognition — “I want A-grade, then I want the higher score in A, then I want the most recent.” Sort the same way every visit, and the reader learns to scan by grade first.
The verify
A one-liner that confirms the order:
curl -sS https://agenticbotsitter.com/reviews/grade/ | \
grep -oE 'class="grade-[a-f]+' | head -20
Expected:
- A-grades at the top, in date-desc within
- Then B-grades, etc.
- Total visible = ~“30” reviews today.
What NOT to do
- Don’t add a
?sort=scorebutton to the UI. The tie-breaker convention is enough. - Don’t add a “Random” sort. Random on a daily-visit mental model is jarring.
- Don’t change the tie-breaker order in a minor release without rerunning the regression suite. The order has been stable since “2026-06”.
- Don’t break the static vs dynamic path distinction.
/reviews/index.astroalways serves the default;/reviews/grade/and/reviews/oldest/are dynamic.
Sources
- ABS live reviews: Botsitter Reviews
- ABS companion: ABS Search Bar: The Cheap State Update, Not a Popup
- ABS companion: ABS RSS and Reader-Friendly Output
- ABS companion: ABS Mobile Nav: The Three Rules
Last verified: 2026-07-14 against the live /reviews/ listing rendered at https://agenticbotsitter.com/reviews/.



Submit a take
Have a different read on this? Drop a comment below — your email isn't published, and I read every one. Nothing leaves the site until I approve it.