Skip to content

Anti-slop evidence pipeline: 25 studied sites, computed rule eligibility, two posts - #65

Closed
nikolas-sapa wants to merge 29 commits into
mainfrom
feat/antislop-authority
Closed

nikolas-sapa wants to merge 29 commits into
mainfrom
feat/antislop-authority

Conversation

@nikolas-sapa

@nikolas-sapa nikolas-sapa commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Builds the evidence pipeline behind ns-ui's anti-slop work, plus the first two posts from it.

What runs

content/evidence/*.md → scripts/build-antislop.ts → mcp/data/antislop.json → get_antislop_rules(context)

Same shape as the three generated pipelines this repo already runs: markdown sources, a build-* script, generated content between markers, a snapshot shipped with the MCP package. No new runtime dependencies.

  • 25 evidence files across 3 cohorts: yc-current (8), agent-tools (10), pre-ai-2015 (7, Wayback 2014)
  • Closed 26-signal vocabulary in lib/antislop-vocab.ts
  • 80 tests, mutation-swept; npm run test:antislop
  • get_antislop_rules MCP tool, stdout byte-empty on boot, 0.19ms serving 103KB against a 50ms budget
  • Snapshot 185KB at the spec's 300-file scale, against a 2MB ceiling

The gate authors zero rules, deliberately

Rule eligibility is computed, not declared: a signal backs a rule only if its present-rate separates exemplar from slop and it is not already common in the 2014 control. The corpus has 2 slop-rated files and the gate requires 3, so every signal reports exactly which evidence it lacks and nothing is authored. A pipeline that refuses to claim what it cannot support is the product working.

Findings that changed the thesis

The convergent template predates AI. centered-hero-stack runs 5/7 in 2014; heroku-2014 runs seven of sixteen slop signals simultaneously. Mean slop barely moves across twelve years (3.14 → 2.62 → 2.40) while mean craft climbs (3.71 → 5.38 → 5.90). They did not abandon the template moves, they stopped running them bare.

Also: a signal cannot be scored from markup. Measured both ways on two sites, source-scoring was wrong on all three signals tried, two in the flattering direction. That kills check_design(html) as a product.

Posts

slop-template-2014-control, source-vs-pixels. Both carry BlogPosting + BreadcrumbList JSON-LD, machine-readable dates, and are now listed in llms.txt — the writing route previously had no schema and the posts were absent from llms.txt entirely.

Caveats, stated in the posts too

n=25. Verdicts and signals were set by the same raters, so the density finding is circular and untested. One viewport, 1440x900.

🤖 Generated with Claude Code


Superseded by #66 — the 28 commits needed DCO sign-off, which required rewriting history. Force-pushing this branch was not permitted, so the signed history went to feat/antislop-authority-signed. Same tree, same content, every commit now carrying Signed-off-by.

nikolas-sapa and others added 29 commits October 10, 2026 14:29
One evidence base, many surfaces. Evidence files record studied sources
tagged by context and cohort; rules aggregate over them with counts
computed from the files, never typed. The same base renders to the MCP,
/guidelines, /writing, /directory, /community and llms.txt.

Follows the pipeline this repo already runs for registry.json, conventions
and llms.txt: source files, a build-* script, generated markers that make
a hand-edited count a red build.

Seven sessions, tracks B (spine) -> D (field study) -> C (corpus) ->
A+E (directory) -> S (surfaces) -> K (community). Test plan with numeric
criteria lands here, before any implementation plan.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
yc-current (8, Fall 2026), agent-tools (5), pre-ai-2015 (7, Wayback 2014).
Clears the spec's seed gate of 12 files and 3 cohorts.

The control cohort inverts the program's starting premise. centered-hero-stack
runs 5/7 in 2014, 2/8 in YC Fall 2026 and 0/5 in agent-tools — it is a 2014
pattern that has been declining, not an AI artifact. feature-icons-identical
and three-card-row follow the same curve. gradient-blob is roughly flat across
twelve years (2/7 then 3/8). The one slop signal genuinely rising is
hero-mockup-float, 3/7 then 5/8.

Craft signals moved the other way: type-scale-high-contrast 3/7 to 7/8,
one-accent-discipline 5/7 to 7/8.

Six of thirteen slop signals never fired in 20 files and are dead-vocabulary
candidates per test 9. motion-tied-to-input is unresolved 17/20 — a static
screenshot cannot settle it, so it needs a different instrument or removal.

Small n. Directional, not conclusive.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Twenty evidence files invalidate a third of the vocabulary written before
any site was studied. Three signals read zero because they are blind by
construction (purple-blue-gradient-cta names a gradient not a hue;
testimonial-card-triplet names an exact count; feature-icons-identical
names glyph identity). Seven patterns the vocabulary cannot express at all,
including artifact-not-dashboard, which exposes real-product-screenshot as
meaning the opposite thing on the best page in the set.

Also records the premise change: centered-hero-stack is a declining 2014
pattern, not an AI artifact, so the program's public claim has to narrow to
one the evidence supports.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nvention

Tested slop-signal count against verdict across all 20 files. Separation is
clean: exemplars never exceed 2 co-occurring slop signals, mixed never
exceed 4, the two slop pages sit at 5 and 6. No single signal predicts a
verdict; the count does. That breaks the v1 rule model, where one rule is
one signal plus a count — such a rule convicts neomatter for centring its
hero and decent-com for floating a product panel, both exemplars.

hero-mockup-float, previously summarized as the one rising slop signal,
fires on exemplars and slop alike and was already 43% standard in 2014. It
is a convention. With it struck, no slop signal in the vocabulary is rising
over twelve years — the ones that move are falling.

Also records disposition for the four remaining zero-firing signals, the
centered-hero-stack problem, and the unruled zero-accent case.

n=20 with 2 slop files. A hypothesis to design around, not a result.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rn its rule

Correcting an over-claim. 'No single signal predicts a verdict; the count
does' was reported as a finding. It is not one: the same rater set the
signals and then assigned the verdict informed by them, so the separation
is partly tautological and may be measuring rater consistency rather than a
property of the sites. Three raters produced the three cohorts and the
pattern holds in each, but self-consistency explains that equally well.
Blind re-rate is session 2 work.

Replaces the blanket 'the rule model does not survive' with a procedure
that keeps working as n grows: a signal may back a rule only if its
present-rate separates exemplar from slop AND it is not already common in
pre-ai-2015. hero-mockup-float fails both and gets no rule.

Settles the third-class question as no. A neutral signal cannot carry a
stance, so it would emit into antislop.json unable to do a signal's one
job. yc-backer-badge is dropped; stock-photo-hero was named at the wrong
altitude and becomes stock-photo-generic.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Recomputing verdicts mechanically from signals alone (slop>=5 slop,
slop>=3 mixed) reproduces only 13 of 20 hand verdicts — and all 7
disagreements run one way: the rater withheld 'exemplar' from a page the
vocabulary scores as clean. Never the reverse, across three raters and
three cohorts.

That one-directional bias argues against pure circularity; a rater reading
back their own signals would err in both directions. Something outside all
22 signals informs the verdict and only ever argues for less praise.
resend-com is the sharpest case: 0 slop, 7 craft, still not exemplar.

Those 7 files are now the most informative in the set, and a better target
for session 2 than the blind re-rate. Also notes that craft count separates
about as well as slop count, which the v1 rule model ignores entirely.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
v2 recorded what the evidence invalidated; this is the resulting list, so
session 2 implements from one file instead of reconstructing a vocabulary
from a revision log.

The structural change: rule-eligibility is computed, not declared. A signal
backs a rule only if the build can show its present-rate separates exemplar
from slop AND it is not already common in pre-ai-2015. centered-hero-stack
and hero-mockup-float are expected to fail and stay as recorded facts. One
namespace, no third class, eligibility tracking evidence rather than an
opinion fixed in October 2026.

16 slop signals, 10 craft, 6 dropped, plus a separate non-stance-bearing
materials key. Names the migration cost: six new signals are unset on all
20 committed files and need a re-pass against the screenshots.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Seven TDD tasks: closed vocabulary module, evidence parser, migration of
the 20 committed files to v3, rule parsing with computed counts, rule
eligibility computed from evidence, the build script and snapshot, and the
get_antislop_rules MCP tool.

Follows the pipeline this repo already runs three times: markdown sources,
a build-* script, generated content between markers, a snapshot shipped
with the MCP package. No new runtime deps, scripts are .ts run by node,
node:test for assertions.

Self-review caught two spec-test gaps and both are patched in: the
dead-vocabulary report (test 9) and the CI diff check that makes a stale
count red (test 5's second half, where only the restore was covered).

Names the migration cost honestly: seven new signals are unset on all 20
files and need a screenshot re-pass, not a sed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
26 signals (16 slop, 10 craft) transcribed from vocabulary-v3, plus a
separate non-stance-bearing materials list. Hand-rolled frontmatter parser,
no YAML dep.

Caught a defect in the plan's own code: the scalar split broke on every
colon and rejoined with ": ", so `source: https://example.com` parsed as
`https: //example.com`. Every evidence file's source is a URL, so all 20
would have been silently wrong, and the plan's happy-path test asserted
only cohort, one signal and prose — it would never have noticed. Fixed to
a first-colon split and the happy-path test now asserts the parsed source.

Each of the 12 tests was seen to fail individually before its
implementation existed, then mutation-verified: 7 mutations, each
reddening exactly its own test. Source restored byte-identical after.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fail, splitFrontmatter, splitKey and inlineList come out of parseEvidence
so rule parsing imports them instead of reimplementing. The plan specified
this logic once, got the scalar split wrong, then told a second agent to
copy the same broken template — one implementation with one set of tests
behind it is the fix.

Extraction, not redesign, verified rather than asserted: all 8 test inputs
probed before and after, every error message and parsed object
byte-identical. Mutation re-checked on the guards that physically moved —
deleting both fence guards inside splitFrontmatter still reddens exactly
the two fence tests, and reverting splitKey to the colon-splitting version
still reddens the happy path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
All 20 re-examined visually — 25 PNGs read, colour calls backed by pixel
sampling rather than eyeballing. Seven new signals set from evidence, not
guessed.

Eligibility caught three design bugs in the plan, all the same root error:
the model assumed every signal was a slop signal.
- Separation was `slopRate - exemplarRate`, negative by construction for
  craft signals, so all 10 and the whole `stance: prefer` branch were dead.
  Now directional by class.
- An empty control pool read as "0% pre-AI", so a signal with no control
  evidence cleared the gate built to catch pre-AI conventions. Now fails
  closed at 3 rated files.

The migration re-adjudicated two files: capveon and silica asserted
real-product-screenshot under v1's looser scope while being artifact pages
with no software UI anywhere. v3's scope fix applied, reasoning recorded in
each file's prose, and the mutual-exclusion test now fails naming the file
if either is reverted.

41 tests green across three suites. Mutation-swept: every check bites,
only the threshold values survive by agreement.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The gate's own reason — "a rule against it would convict the control
cohort" — is an avoid-rule concern. A prefer rule built on
one-accent-discipline at 71% pre-AI praises the 2014 cohort; there is
nothing to convict. It was written when every signal was assumed to be a
slop signal, the same root error as the separation direction.

Not inverted for craft: requiring pre-AI prevalence would assert real craft
must be timeless, a stronger claim than the evidence supports, and would
disqualify a genuinely new technique like live-claim-control.

Changes no outcome today — all 10 craft signals now clear the control gate
and stop at the verdict-pool minimum instead, so the corpus still reports
zero of 26 eligible. What changes is the diagnosis: 22 of 26 now say "not
enough rated evidence" rather than a verdict the evidence cannot support.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Set from a static capture, then checked with a scroll walker and
contradicted. Across the 2630px below y=3300 the maximum deviation from
the #fafafa field is zero pixels, under a walk that makes nxtcure-labs go
11% -> 66% inked and openhack paint its whole page. The document does grow
5333 -> 5930px, so something mounts, but scrolling is the gate this signal
names and scrolling does not open it.

The dead region is established; a scroll-triggered reveal as its cause is
not. Hydration that never completes headless, an observer wanting real
user activation, and a bot gate all fit the evidence equally.

Note for whoever authors a rule on this signal: the reversal removes the
only exemplar carrying it, so all four remaining cases are mixed or slop.
That makes the signal look more discriminating than the evidence supports —
it is partly an artifact of the one counterexample being downgraded.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
stripe.com, railway.com, clerk.com, modal.com, browserbase.com — the five
left unstudied when that agent hit a budget error. Cohort is now 10.

No new slop verdicts: 3 mixed, 2 exemplar. The slop pool stays at 2 and
every signal stays ineligible. The rater was told a slop verdict would
unblock the separation test for all 26 signals at once, and rated honestly
anyway, which is the result worth having.

Two corrections to what the earlier wave reported. Stripe is a
counterexample on ornament but is NOT the worst sheet in the cohort —
Railway is, 7 slop signals to Stripe's 4. And the brand-colour logo wall
is not unique to Stripe; Browserbase ships the same pattern, so any rule
derived from it needs both files.

First real test of v3's signal split, and it holds: Stripe's CTA is a flat
rgb(83,58,253), so indigo-violet-accent is present and gradient-fill-cta
absent. The site that actually ships a gradient CTA is Railway. Under v1's
purple-blue-gradient-cta both would have read the same.

Every signal resolved on all five, via a second measurement pass rather
than concession: no-JS plus reduced-opacity for scroll-reveal-blank,
light-vs-dark render diff for materials, direct pixel sampling for Clerk's
declared-but-unpainted gradient.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
v3 was decided from 20 files rated under v1. This records where v3 bent
when four agents rated 25 files against it directly.

The largest finding closes a question the program spec deferred: a signal
cannot be scored from source. Measured both ways on clerk-com and
railway-com, source-scoring was confidently wrong on all three signals
tried — a declared gradient blob that paints nothing, sixteen blur strips
that are one progressive-blur mask, and eight U+2713 inside JetBrains Mono
build logs that a regex would score as emoji. Two fail in the flattering
direction and the third would corrupt emoji-as-icon, the zero every other
zero is judged against. So check_design(html) as markup analysis is dead;
anything automating the rating has to render and sample pixels.

Also records that scroll-reveal-blank conflates the reveal mechanism with
agent-readability (the two barely correlate across five sites),
that real-product-screenshot rewards the weaker artifact over a live
embed, the canvas dwell-time capture failure, the one-directional test
artifact, and where the third slop file is not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Completes the pipeline. 76 tests green, typecheck clean, stdout byte-empty
on MCP boot, latency four orders of magnitude inside the spec's budget.

Two of the plan's three guard checks could not fail as written, which is
the failure the "verify the check can fail" rule exists to catch:

- Deleting an evidence file exits 0 on the real corpus. Not broken —
  vacuous, because no rule exists to depend on it. Replaced with a fixture
  where a rule names a deleted file (exit 1) and one where an eligible
  signal has no rule (exit 1).
- The unknown-signal check used `echo >>`, which appends past the closing
  frontmatter fence, so the injected line was prose and never parsed.
  Replaced with an injection inside the signals block (exit 1).

The CI stale-count line had the same problem twice over: nothing in
content/rules/ is tracked yet, and mcp/data/ is gitignored, so half the
pathspec compared against nothing. Proven red in a throwaway repo instead —
stale count exits 1, correct count exits 0, hand-edited 999/999 exits 1.

TDD caught a real bug: the snapshot stored each rule body as parsed, so the
MCP would have served "0/0 files studied" while content/rules/ said 3/6.

Snapshot is 161 bytes at 0 rules; stress-measured at the spec's 300-file
scale it is 185,813 bytes, 9.3% of the 2MB ceiling.

mcp/data/antislop.json is deliberately not committed — mcp/data/ is
gitignored, same as registry-snapshot.json.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The build pipeline could not be tested against the real corpus: zero of 26
signals are eligible there, so the eligible-and-unruled loop never fires,
content/rules/ stays empty and the build passes trivially. The fixture
corpus gives it 3 slop, 3 exemplar and 3 control files so at least one
signal reaches eligible and the counting, marker-rewriting and snapshot
paths actually run.

Committed rather than generated at runtime. A fixture built by the test is
a target the test controls — the same stale-green configuration this
project keeps catching. Committed files get reviewed, diff when changed,
and are fed to the real parseEvidence, so the chain exercises the actual
parse path rather than hand-built objects.

Re-proven to bite after the plumbing change, since green tests over new
fixtures prove nothing until a mutation reddens them:
- fixture slop verdict flipped to exemplar -> 7 fail
- unknown signal in a fixture             -> 19 fail
- fixture evidence file deleted           -> 8 fail
Fixtures diffed byte-identical after, 31/31 restored.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
80 tests green, build wired into registry:build, MCP tool serving. Zero of
26 signals eligible: the corpus has 2 rated slop files and the gate
requires 3, so nothing is authored because nothing can be supported.

Names the session 3 target precisely — one more slop-rated site opens the
separation test for all 26 signals at once, and it is not in agent-tools,
where the template signals those sites carry are ones they originated.

Records the unfinished bidirectional walk and the metric fix it needs: a
continuous per-row ink fraction rather than a boolean, since one line of
text currently marks a row fully inked, which is exactly how a false
absent survives.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Post reports that a signal cannot be scored from markup: three signals
measured both ways on clerk-com and railway-com, wrong on all three, two
in the flattering direction.

Writing it caught errors in the sources it was citing.

clerk-com.md claimed "sixteen backdrop-filter surfaces, five above the v3
threshold of three" — conflating blur radius above 3px with the signal's
actual threshold of 3+ surfaces. Sixteen against three is thirteen over,
not five. The count claim is dropped rather than recomputed, since the
measurement that matters is that the sixteen strips are one progressive
mask.

The v4 spec had the capture parameters wrong twice: 700px steps where the
script and two other docs say 600px, and a ~12s canvas dwell where
modal-com.md says ten seconds.

Rescued one measurement's provenance. The #f5f3ff finding — an absolute
brightness threshold reads a light page as 100% inked before and after —
existed only in a script header under ~/scratch, which any cleanup would
have taken with it.

Softened the post's own weakest sentence, which its author flagged
unprompted: "a tool that scores pasted HTML cannot work" is an absolute
built on three signals across two sites. Now states the direction the
measurements point and names the thin basis.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Recounting every signal across the completed cohorts (n=25, v3 vocabulary)
contradicted two things the v2 spec asserted from partial data.

three-card-row is NOT declining. 2/7 in 2014, 1/8 in YC Fall 2026, 5/10 in
dev tools. It is rising. The spec claimed it followed centered-hero-stack's
curve; it does not. centered-hero-stack also does not reach zero — 3/10 in
agent-tools, not the 0/5 measured before that cohort was completed.

The density separation did not survive either. At n=25 the slop band [6,7]
sits entirely inside the mixed band [1..7]: railway-com carries 7 slop
signals, the same as heroku-2014, and is rated mixed. Caused by five added
files plus two v3 signals that moved heroku 6->7 and openhack 5->6.

What separates instead is slop NET of craft, with no overlap: exemplar -7
to -2, mixed -5 to +4, slop +4 to +5. railway-com nets +1 on 7 slop and 6
craft; heroku-2014 nets +5 on 7 slop and 2 craft. Same slop count, opposite
verdicts, craft the whole difference. Still circular, and stripe-2014 nets
+4 while rated mixed, so it is a better hypothesis rather than a finding.

So "the strongest builders moved past the template" is not supportable.
What is: they did not abandon the template moves, they stopped running them
bare. Mean slop barely moves across twelve years (3.14 -> 2.62 -> 2.40)
while mean craft climbs (3.71 -> 5.38 -> 5.90).

heroku-2014 now reads seven of sixteen, not six of thirteen — its prose was
written against v1 before indigo-violet-accent was added.

Also qualified the post's own weakest sentence, flagged by its author: the
cohort ordering on mean slop is a half-signal gap on single-digit samples.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
BlogPosting + BreadcrumbList on app/writing/[slug], Blog + BreadcrumbList
on the index. Matches the jsonLdScript() pattern already in
app/categories/[id]/page.tsx. The writing route was the only page type in
the app with no JSON-LD while components, categories, home and layout all
had it.

Two gaps found beyond the one I identified: the writing index had no schema
at all, and scripts/build-llms.ts never mentioned writing, so the posts
were absent from llms.txt and llms-full.txt entirely. Both fixed; the llms
builder imports the existing loadWritingPosts() rather than re-parsing
frontmatter.

Validated against the downloaded schema.org vocabulary rather than by eye:
rendered HTML fetched from a dev server, every ld+json block json.loads'd
from the real bytes, every @type and property resolved through the
rdfs:subClassOf chain with domainIncludes checked. 15 blocks, 36 nodes,
121 properties, all resolve. Cross-page @id references confirmed to
resolve rather than dangle.

Then proven able to fail: injecting totallyFakeProperty, a bogus @type and
reviewCount were all flagged — the last with "reviewCount not valid on
BlogPosting (domain: ['AggregateRating'])", which is exactly the
fabricated-review-count failure mode. Restored, re-run green.

Fields omitted for lack of a real value rather than invented: dateModified
(frontmatter carries one date; a file mtime is a checkout artifact),
keywords, articleSection, wordCount (table-heavy posts would count pipes
and ## as words). author is sourced from app/about/page.tsx, which already
states the site is maintained by Nikolas Sapalidis.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The freshness gap was narrower than it looked and real. Both pages already
rendered the date visibly, but as bare text in a <p> with no machine-
readable attribute, so a crawler could not parse it. Now <time dateTime>.
Same data, extractable.

For "last updated" the schema wants a dateModified the frontmatter does not
carry. Rather than invent one from a file mtime, which is a checkout
artifact and not an editorial revision, lib/writing.ts gains an optional
`updated:` key that drives both dateModified and a visible "Updated" line.
No post declares it, so dateModified is absent everywhere today and stays
absent until the owner adds the key.

FAQPage deliberately not emitted. llms-txt-use-when-audit.md does carry
five genuine question-form headings, but FAQPage is a page-level type
declaring a page's main content IS a list of Q&As, and that post is a
narrative research report using questions as section headers. Emitting it
would compete with BlogPosting as the primary type and misdescribe the
document — misrepresentation rather than fabrication, but the same failure
class.

Organization/sameAs needed nothing: lib/site-identity.ts already carries
three genuine sameAs URLs, a founder and real contact points, and the new
publisher @id links into it rather than duplicating it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every post used only `###`, so pages rendered h1 then h3 and skipped h2
entirely — the documents had no top-level section tier for an extractor to
read a structure off. post-body.tsx maps `##` to h2 and already styles it;
nothing was using it.

Pure markup. Zero words changed, 25 heading markers only, and the rendered
styling is the h2 class the component already carries.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…blank present

The signal had been verified only on files already marked present, which
can catch a false positive but never a false negative. It caught two, both
exemplars, so the signal appeared to separate perfectly on a 3-file base.
Walking the 20 unverified files finds the artifact was real and ran in the
flattering direction.

4 changed, all absent -> present, nothing the other way:
- silica (exemplar)          y4800-5100 0.0% -> 53.5%, +16/-0 rows
- browserbase-com (exemplar) y4640-5080 0.6% -> 61.0%, +52/-0
- neomatter (exemplar)       36 of 36 faded nodes cross, 1028ch
- railway-com (mixed)        820px band 17.9% -> 68.7%, 38 nodes cross

scroll-reveal-blank is now 9 present / 2 unresolved / 14 absent, and
exemplars carry it at 3/9. It does not separate.

The instrument was rebuilt before measuring, since the prior near-null
came from a boolean row metric where one line of text marks a row fully
inked. Now a continuous per-row ink fraction over 180 sampled columns,
same-run static controls, and a DOM text-opacity measure splitting crossed
nodes from design fade and from nodes absent at rest. Calibrated both ways
first: openhack reproduced its known present, vercel-com its known null,
and the DOM measure independently returned exactly the 44 crossings
clerk-com's prose already recorded.

A dwell control proved load-bearing: a 10000px walk takes ~20s against an
8s control, so animated demos mount only in the walked capture. It
corrected the rater twice in both directions.

Three archive.org artifacts caught rather than inherited: squarespace-2014
first read 22.56x because the control served a blank page (0.99x on
retry), heroku-2014 13 gained rows (0 on retry), dropbox-2014's 1.24x was
the Wayback toolbar sparkline. All three stay absent. A same-run control
fixes page drift but not per-context resource failure; only the retry
caught those.

Known gap: databuddy had walk and park but no dwell control, left absent
at 6 crossings / 130ch with flat pixels.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Running `npm install` to get a typechecker into this worktree rewrote the
lockfile, removing @auth/core 0.41.3 and 60 other lines. @auth/core is a
peer dependency of @convex-dev/auth, so a fresh `npm ci` would not have
installed it and convex/auth.ts would fail to resolve
@auth/core/providers/github and /google on any clean checkout. That was
committed in a02ab41 and reported by two agents as "three pre-existing
convex/auth.ts errors" — they were not pre-existing, they were mine.

Restored from origin/main and reinstalled with `npm ci`, which installs
exactly what the lockfile pins and does not mutate it. Typecheck is now
clean with zero errors project-wide, where it previously reported three.

Use `npm ci` in a worktree, never `npm install`, unless the intent is to
change dependencies.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Eight of nine unsearchable headings rewritten to name the entity, metric
or cohort an assistant can match against a query. "The counts" labelled
the section holding the 5/7 vs 2/8 numbers — the highest-citation-value
block in the corpus — and named no cohort, no metric and no subject.

The register target was the author's own back catalogue:
llms-txt-use-when-audit.md already runs five literal question headings, so
these match a standard already set rather than one invented. No heading in
any of the four posts uses backticks, so signal names stay bare.

Kept as written: "Two of these flatter. The third corrupts the
instrument." It is the best line in either post, the section below it
already names the signal, and the drafting agent flagged its own
replacement as the one most likely to be rejected. It was right.

Six statistic passages split so each lands near the 40-60 word window AI
assistants extract. Two are pure paragraph breaks with zero words changed.
Four restore a fact from the sentence next door: an antecedent ("mean ink
per row" named no page, "it fires on three pages" named no signal) or a
denominator, so no extractable passage now reports a count without its n —
3.14/2.62/2.40 each carry n=7/n=8/n=10, and "three pages" becomes "three
of the seven 2014 pages".

No number, claim or caveat changed. Some mechanism descriptions remain
over 60 words by design: their figures are inline detail rather than the
claim a reader would lift.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The merge of main brought scripts/test-writing-markdown.ts, which
immediately caught a real shipping bug in slop-template-2014-control.md:
post-body.tsx implements no table, so that post's evidence table — the
counted centrepiece, eleven rows of signal rates across three cohorts —
would have displayed as literal `| centered-hero-stack | 5/7 |` pipes on
the live site. Valid HTML, so nothing else would have caught it.

Implemented the renderer rather than degrading the table to prose. A
comparison table is also the most extractable shape for an assistant to
cite, so prose would have cost the post its most citable block.

GFM subset matching what the posts actually author: header row, `|---|`
delimiter, body rows, optional outer pipes. No alignment colons, no inline
pipes in cells, no caption — nothing in content/writing uses any of them.
"table" added to the gate's SUPPORTED set, which is the protocol that file
documents for itself.

Verified by rendering, not by reading: the real page through a dev server
returns 1 table, 5 th, 36 td and zero literal pipe rows, alongside 7 h2
(the heading promotion), 6 ld+json blocks and the machine-readable date.
The gate was also re-proven able to fail: an injected blockquote is caught
by file and line, then reverted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
ns-ui Ready Ready Preview Oct 13, 2026 3:44am UTC

@nikolas-sapa

Copy link
Copy Markdown
Owner Author

Superseded by #66, which merged the same tree with DCO sign-off. All of this work is on main: the pipeline landed in #66, the evidence and rules in #68, and the handoff in #71.

@nikolas-sapa
nikolas-sapa deleted the feat/antislop-authority branch October 13, 2026 12:46

This branch was successfully deployed

1 active deployment
Preview — 275f89b3 Deployed Oct 13, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant