Repository navigation
Anti-slop evidence pipeline: 25 studied sites, computed rule eligibility, two posts - #65
Closed
nikolas-sapa wants to merge 29 commits into
Closed
nikolas-sapa wants to merge 29 commits into
nikolas-sapa wants to merge 29 commits into
Conversation
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>
# Conflicts: # package.json
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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Owner
Author
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.yc-current(8),agent-tools(10),pre-ai-2015(7, Wayback 2014)lib/antislop-vocab.tsnpm run test:antislopget_antislop_rulesMCP tool, stdout byte-empty on boot, 0.19ms serving 103KB against a 50ms budgetThe 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-stackruns 5/7 in 2014;heroku-2014runs 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 carryBlogPosting+BreadcrumbListJSON-LD, machine-readable dates, and are now listed inllms.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 carryingSigned-off-by.