pseo-quality-gate
v0.1.0
Published
Premium-content quality gate for programmatic SEO at scale. 13 hard gates that distinguish premium pSEO content from AI-slop templates.
Maintainers
Readme
pseo-quality-gate
Premium-content quality gate for programmatic SEO at scale. Thirteen hard gates that distinguish premium pSEO content from the AI-slop templates that get classified out of Google's index.
The problem
Programmatic SEO operations that ship at scale (>100 pages from a single template) face a specific Google failure mode. The helpful-content classifier waits two to twelve weeks before applying a site-wide quality verdict, and once it triggers, the entire programmatic surface drops out of the index together. Individual page recovery after a site-level demotion takes months and sometimes never happens.
The Detailed.com 2024 audit of 23 large affiliate sites found that 17 had lost 60-95% of organic traffic within 90 days of a major Google update. The surviving 6 had measurably stricter content quality controls in place pre-update.
Writing better content is the standard advice. Enforcing "better" mechanically before pages ship, at the scale where humans cannot review every page individually, is the hard part.
What this package does
Validates a JSON content payload against thirteen hard gates that approximate the helpful-content classifier's filter from the public side. The gates are necessary, not sufficient. Passing every gate does not guarantee ranking; failing any gate is a strong predictor of eventual deindexation.
The validator is content-generator-agnostic. It fires on the output of whatever pipeline you use (human, LLM, hybrid) and tells you whether the page passes the structural and stylistic gates. It does not generate content, score backlinks, or validate schema.org markup; those are separate concerns with their own tools.
Quick start
# Run via npx, no install required
npx pseo-quality-gate ./content/page-001.json
# Validate a directory tree
npx pseo-quality-gate --glob "content/**/*.json"
# Machine-readable output for CI pipelines
npx pseo-quality-gate --json ./content/page-001.jsonOr as a library:
import { runAllGates } from "pseo-quality-gate";
import fs from "fs";
const data = JSON.parse(fs.readFileSync("page.json", "utf8"));
const result = runAllGates(data);
if (!result.pass) {
for (const e of result.errors) console.error(`${e.gate}: ${e.message}`);
process.exit(1);
}The thirteen gates
| # | Gate | What it checks |
|---|---|---|
| 1 | title | length ≤ 70 chars |
| 2 | metaDescription | length 130-165 chars |
| 3 | h1 | present |
| 4 | about | ≥ 4 paragraphs, ≥ 500 words total |
| 5 | faqs | ≥ 5 items, each answer ≥ 15 words |
| 6 | citations | ≥ 1 with valid reference id (when embeddedItems[] is non-empty) |
| 7 | relatedLinks | ≥ 5 internal links |
| 8 | author | name and url present |
| 9 | lastUpdated | YYYY-MM-DD format |
| 10 | emDashes | zero anywhere in the JSON |
| 11 | marketingProse | no blocklisted terms in body content |
| 12 | marketingHeadlines | no blocklisted terms in title/h1/subheading/metaDescription |
| 13 | solicitCriticism | no fake-humility closers anywhere |
Full gate-by-gate reference: docs/13-hard-gates.md.
Schema
The validator works on a generic premium-content shape. Map your domain fields to this shape; the gates do not require any specific vertical.
interface PremiumContentPage {
title: string; // <= 70 chars
metaDescription: string; // 130-165 chars
h1: string;
subheading?: string;
about: string[]; // >= 4 paragraphs, >= 500 words total
faqs: { q: string; a: string }[]; // >= 5
citations?: { id: string; text: string }[]; // required if embeddedItems[]
embeddedItems?: { id: string; [k: string]: any }[];
relatedLinks: { url: string; label: string }[]; // >= 5
author: { name: string; url: string };
lastUpdated: string; // YYYY-MM-DD
}See examples/pass.json for a complete passing payload and examples/fail.json for a deliberately broken one.
Configuring thresholds
import { runAllGates, DEFAULT_THRESHOLDS } from "pseo-quality-gate";
const result = runAllGates(data, {
thresholds: {
...DEFAULT_THRESHOLDS,
ABOUT_MIN_WORDS: 750, // stricter than default 500
FAQ_MIN: 7, // stricter than default 5
},
extraProseTerms: ["unrivaled", "world-renowned"], // extend the blocklist
});Using individual gates
import { gateAbout, gateMarketingProse, gateEmDashes } from "pseo-quality-gate/gates";
const errors = [
gateAbout(data),
gateEmDashes(data),
gateMarketingProse(data, { extraProseTerms: ["myextra"] }),
].filter(Boolean);Origin
This validator was originally written for the day-trip programmatic SEO section of bestroadtrip.com, where it gates eighty city-pair pages built from a corpus of 1,400 creator-recorded short videos. The site's research section publishes the underlying methodology and dataset behind the page generator; the gates here are extracted from that specific operation and generalized for any premium pSEO content type.
Security and privacy
This package is a CLI/library that reads local JSON files and runs deterministic checks. It makes zero outbound network requests. It uses zero npm dependencies. It does not collect telemetry, write to any path outside the working directory it is run in, or contain any embedded credentials, API keys, or proprietary data. The validator can be run fully offline against any content payload.
Contributing
Contributions welcome. New gates that have empirical backing, refinements to the default thresholds, or expansions to the marketing-language blocklist are all in scope. See CONTRIBUTING.md.
License
MIT. See LICENSE.
