Skip to content

Repository files navigation

anti-slop

Read an interface, or a piece of writing, for the marks of work nobody finished.

Website · Discussions · Install · How it works · Contribute

version 0.6.0 94 tells 4 skills MIT license

A Claude Code plugin for work that came out generic. It ships four skills, and 94 tells: 54 that read an interface, 40 that read prose.

anti-slop:audit reads interface code for the marks of work nobody finished: the palette nobody picked, the dark theme nobody opened, the copy nobody wrote, the settings nobody set. Fifty-four tells across five axes, reported as a ranked list with a file and a line. The report uses plain words, so the reader does not need the vocabulary to act on it.

It reads any web stack. Most of the catalog never mentions a framework, and the part that does names Tailwind classes as examples of a pattern rather than as the pattern itself.

anti-slop:build runs before the components exist. It forces the decisions whose absence the auditor detects, and writes them where the auditor looks for them.

Before anything else it asks how large the job is, and says in one line which answer it took. A page or a site that does not exist yet runs the whole process. A screen inside a system that already has a theme reads its roots off what shipped and builds against them rather than redeciding them. A single element gets only what that element owes. The floor holds at every size, because a focus ring nobody declared is the same defect in a one element job as in a whole site.

It works in three registers, and the difference between them is what stops it being either a form or a generator. Anything with one right answer it settles in silence: the floor, the arithmetic, the craft. The shape of the page it brings as two or three named routes, one of them recommended, and then it takes its own recommendation and builds. It stops for an answer in two cases only: you asked to choose, or a directional root came back unanswered and the routes differ on exactly what it would have settled. Anything bolder that would open a different design line it proposes, with what it costs and what it needs from you, once there is a page to look at. The line is testable: a decision is directional if changing it later means rebuilding rather than editing.

Where the session has browser tooling it opens the page before it finishes, at 375, 768 and 1440, in both themes, under reduced motion, with one tab pass. Two of its checks cannot be answered by reading and this is how it answers them.

anti-slop:text rewrites prose. Forty tells across five axes on the text itself rather than on the interface around it: whether anything is behind the sentence, whether the content chose the shape or the mould did, whether anyone chose the words, whether anyone set the punctuation, whether a person is in the text at all. It returns the rewritten text and no report. It reads English and Portuguese, and the Portuguese half is written from Portuguese rather than translated from the English one.

anti-slop:fix repairs an interface from findings that already exist. skills/fix/references/repairs.md maps every one of the fifty-four interface tells to the rule that repairs it, and the skill repairs the cause a finding names rather than the row it was printed on. It refuses by name the repairs that need an answer only the person has: a root nobody set, a fact nobody supplied, a redesign, or a change with no bounded size. It re-audits every repair before calling it done.

The auditor and the builder are one loop. The auditor suppresses a false positive when it finds evidence that somebody chose the value. That evidence may live in theme.extend, custom properties, a tokens file or primitives that differ from stock. The build skill writes into those four places. So a tell firing on a tree the build skill produced is the build skill's failure, and it arrives with a file and a line. That is how this repository tests the half of itself that generates rather than detects.

The auditor and the fixer are a second loop, and it is the one that returns to the code rather than to a specimen: a report becomes a repair, and the fixer re-audits the same finding before it is allowed to call anything done.

The claim it does not make

Nothing in either catalog proves a model wrote the code, or the text. It proves nobody came back to it. A tell fires on absence and repetition, not on origin, and the same absence shows up whether a person typed every line or an agent did. That is also what makes the rewriter safe to run on your own writing: a catalog framed as a detector has to accuse before it can help, and it will accuse a careful writer who happens to like the word landscape.

The skills never write that a file was AI-generated, vibecoded, or generated by any tool. They report what is missing, not who (or what) left it that way.

How it works

skills/audit/ ... the auditor, catalog included
   SKILL.md ......... the procedure: how to rank, what to load, how to report
   |
   +-- references/surface.md  A1-A14   the palette, radius, shadows, type
   +-- references/craft.md    C1-C16   the rendered result
   +-- references/states.md   S1-S3    the paths off the demo
   +-- references/words.md    W1-W8    the copy
   +-- references/finish.md   F1-F13   lang, title, meta, keys, legal pages
   +-- references/molds.md             recurring shapes across tells
   |
skills/build/ the four roots, and what derives from them
   SKILL.md ......... the process, the seven recorded shapes, the two passes
   +-- references/deriving.md          the rule per derived value
   +-- references/floor.md             what no brief answers differently
   +-- references/legal.md             the privacy notice and the terms
   +-- references/composing.md         the ground, the largest element, routes
   +-- references/precedents.md        measured moves, cited after deciding
   |
skills/text/ ... the rewriter, catalog included
   SKILL.md ......... the rule, the three roots, the removal pass, the modes
   |
   +-- references/hollow.md    H1-H9    nothing behind the sentence
   +-- references/template.md  T1-T9    the shape the mould chose
   +-- references/grain.md     G1-G10   the words nobody picked
   +-- references/marks.md     M1-M6    the punctuation nobody set
   +-- references/presence.md  P1-P6    whether a person is in the text
   +-- references/vocabulary-en.md      the watched words, English
   +-- references/vocabulary-pt.md      the watched words, Portuguese
   |
skills/fix/ ..... the third side of the loop, repairs the audit's findings
   SKILL.md ......... the rule, the refusals, the process, the invocation table
   +-- references/repairs.md           every interface tell mapped to its rule
   |
fixtures/ ........... four interface specimens, two clean, two slop
corpus/ ............. four prose specimens, one pair per language
calibration/ ........ the blind reports, as the runs produced them
hooks/ .............. the SessionStart note that routes work to the skills
scripts/validate.py . structural check over both catalogs
BACKLOG.md .......... what the last round left open, and what gates it
ROADMAP.md .......... the order the open work should happen in, and why

54 tells across five axes is far more than belongs in one context window, and loading all of it to answer anti-slop words would crowd out the code being audited. So SKILL.md carries only the procedure and a table mapping each invocation to the references it needs; the axis files load on demand. A single axis run reads one file.

The ordering is deliberate rather than alphabetical. Finish runs first because nearly all thirteen of its tells are greppable, so the cheapest evidence is gathered before the axes that require reading relationships between files.

Every tell is written to the same four field shape, Signal, Principle, Fix and Not slop when. The last field is the one that makes the catalog usable: a tell without a stated exemption becomes a lint rule that fires on deliberate choices, which is how audit tools lose their readers.

None of the four has a list of banned patterns

Forbid the purple gradient and the generic reappears wherever the list does not reach. The audit rule says why: a finding is a pattern present and no evidence anyone chose it. The pattern was never the defect.

Both fixtures named clean-* in this repository are the argument. clean-landing runs a gradient and a shadow-xl, just like the slop landing. Its colours come from the theme and each effect appears once. A2 and A4 are on its forbid row and neither has ever fired on it in a blind run.

There is one class of rule that no brief releases, and naming it costs the sentence above nothing. A page has a language. A page has one h1. A focus ring is visible, contrast clears its floor, a fetch has a failure branch, and a public site that collects something says so. F1 and F6 have always answered Not slop when with Never, so the class existed before it had a name; the floor round of 2026-08-22 named it and let contrast, focus, the states beyond success and the legal pages join. None of those forbids a pattern. Each fires on an absence, which is what the rule above says the defect always was. skills/build/references/floor.md is the whole list, and it is deliberately the shortest file in the plugin that could be called a checklist.

The rewriter inherits the same rule, and it costs more there than anywhere else, because the neighbouring tools do ban. The em dash is the clearest case. It is a reliable marker of generated prose and it is also ordinary punctuation that editors and journalists have used for two centuries, so banning it scrubs the tell and the author together. The rule fires on the dash as a text's default joint with nothing else decided either, and it stands down when a sample of the author's writing uses dashes at that rate. The one exception is a rule the project itself declares, which is evidence that somebody decided and therefore outranks the author's habit inside that project. This repository declares one: scripts/validate.py forbids dashes in every README* and under site/ and specimen/.

The build skill works alongside the design skills rather than replacing them, and the boundary is narrower than this repository used to claim: a survey of frontend-design and its neighbours found the two reaching for the same palette count and the same naming rule, and disagreeing on type families. Those skills handle composition: the shape of the page and the element the reader remembers. The build skill records the decisions, which none of them does.

So the build skill does not tell you to avoid anything. It makes you decide what the product is, how it speaks, how sober it should look and how much belongs on one screen. It derives the rest from those answers and records the reasoning beside each value. A gradient is available. Reaching for one without deciding it is not.

Install

Install it as a plugin. From within Claude Code, point a plugin marketplace at your checkout and install from it:

git clone https://github.com/luantaraschi/anti-slop
/plugin marketplace add ./anti-slop
/plugin install anti-slop@anti-slop

To install one skill on its own, without the plugin, copy its directory where Claude Code looks for personal skills. Each catalog travels inside its own skill:

cp -r anti-slop/skills/audit ~/.claude/skills/audit
cp -r anti-slop/skills/text ~/.claude/skills/anti-slop-text

Rename the text skill on the way in if you already have a skill called text. The directory name is what Claude Code matches on, and the frontmatter name is what the plugin uses.

Firing without being asked

Installed as a plugin, hooks/hooks.json registers a SessionStart hook that prints five sentences routing interface work to anti-slop:build, a review to anti-slop:audit, a repair to anti-slop:fix and prose to anti-slop:text. It is about 310 tokens, it loads no catalog, and it is the only part of the plugin that costs anything in a session it has nothing to do with. That is deliberate: the note routes and does not teach, so everything it would otherwise say stays in the skill files and loads only when a skill is actually invoked.

hooks/run-hook.cmd is a polyglot wrapper copied byte for byte from the one plugin on the author's machine whose SessionStart hook is known to work on Windows. It exists because Claude Code prepends bash to any command containing .sh, which is why the payload script is extensionless, and because a CRLF checkout makes bash read the trailing carriage return as part of the last argument and the hook then fails silently. .gitattributes pins both to LF for that reason.

Copying a skill directory on its own does not bring the hook. Without it the skills still fire on their descriptions, which is what they were doing before. The honest note is that they were not firing often enough, and that is what the hook is for.

Use

Type any of these as a prompt to Claude Code. anti-slop is not a slash command; it is the phrase that matches the skill's description and starts the audit.

Invocation Axes References to load
anti-slop all five every axis file, plus molds.md
anti-slop surface Surface surface.md, molds.md
anti-slop craft Craft craft.md
anti-slop states States states.md
anti-slop words Words words.md
anti-slop finish Finish finish.md

A path alongside the mode restricts the scope to that file or directory. Without a path, the target is the project root.

The build skill has no modes. It fires on its description, when you are about to build an interface or when one already reads as generic and its palette, type scale, radius, motion and copy need deciding rather than inheriting. Give it the brief. It will tell you which of the four roots the brief does not answer rather than filling them in silently.

The text skill takes the same shape as the auditor, one axis at a time, and returns the rewritten text rather than a report.

Invocation Rewrites References to load
anti-slop text all five axes every axis file
anti-slop text hollow Hollow hollow.md
anti-slop text template Template template.md
anti-slop text grain Grain grain.md
anti-slop text marks Marks marks.md
anti-slop text presence Presence presence.md

The vocabulary file for whichever language the text is in loads on every invocation. A path alongside the mode is the target; without one, the target is whatever text is in the conversation.

Give it a sample of your own writing if you have one. It outranks every tell in that catalog, including the one about dashes, because matching the author beats scrubbing the tell.

anti-slop words and anti-slop text are not the same thing. The first reads the copy inside a running interface, a button label or an empty state, and reports. The second reads prose and rewrites it. A landing page has both.

The fix skill takes findings, from a report already in the conversation, from a file, or from a fresh audit it runs itself, and repairs the cause each one names.

Invocation Repairs References to load
anti-slop fix every finding it is handed or finds repairs.md, then the rules it names
anti-slop fix surface Surface findings only repairs.md, then the rules it names
anti-slop fix craft Craft findings only repairs.md, then the rules it names
anti-slop fix states States findings only repairs.md, then the rules it names
anti-slop fix words Words findings only repairs.md, then the rules it names
anti-slop fix finish Finish findings only repairs.md, then the rules it names

A path alongside the mode restricts the scope. It repairs interface findings only. The text catalog needs no fixer, because anti-slop text already returns the rewritten text rather than a report.

Example output


Verdict: a stock shadcn dashboard whose primitives were installed and never touched. Card and Button sit untouched in components/ui/ while every card, input, and button elsewhere in the app reimplements their classes raw. The empty theme.extend never picked a color, radius or font. Metadata was set up once for the whole app instead of per route, so title, description, and canonical are all missing together. The invoice table renders its rows without a key. The copy holds up everywhere except the empty invoices table, which reports a count instead of inviting the first action.

ROOT

id finding location
A10 Card and Button reimplemented raw wherever they're actually used components/stat-card.tsx:3 (also components/table.tsx:6, app/page.tsx:34) fixes A4, A6
A1 Theme extend is empty; text-gray-500 is the only secondary color anywhere tailwind.config.ts:5 fixes A3, A5
F2 One metadata block covers both routes, with no description or canonical app/layout.tsx:1 fixes F3, F4, F9

THEN

id finding location
F1 <html> has no lang attribute app/layout.tsx:5
F11 Invoice rows render via .map with no key prop components/table.tsx:8
W3 Empty invoices table reads "No items found," no direction to add one components/table.tsx:3

Detail on the two Surface roots: components/ui/button.tsx and card.tsx are byte-for-byte copies of the shadcn install. The full variant list is present (destructive, outline, secondary, ghost, link, plus sizes sm, lg and icon), but none of it ever renders. <Card> gets imported and used exactly once (app/page.tsx:25). StatCard, InvoiceTable, the search box and the "Export CSV" button are raw elements that retype rounded-2xl border border-gray-200 shadow-lg p-6 by hand. That also explains A4 and A6. Each callsite made the same choice independently instead of inheriting it from one component. Routing those five spots through the real Card and Button fixes A10 and collapses A4 and A6 with it.

The theme side is separate. theme: { extend: {} } is the whole config, so there is no color, radius scale or type scale to point to. font-bold is the only emphasis used anywhere. rounded-2xl is the only radius on cards, inputs and the hand-rolled button. That single empty object is why A1, A3, and A5 all fire from the same piece of evidence.

The Words axis is otherwise clean. It has no catalog labels, leaked implementation names or inflated copy. The not-found page (app/not-found.tsx:8, "That address is not part of this workspace") is specific rather than apologetic.


That is the output of a blind audit of fixtures/slop-dashboard, one of the four calibration specimens, pasted exactly as the run produced it. It ran against that fixture as it stood at v1, before the Craft extension lengthened the tree and moved its line numbers, so every citation above points at where the code was then rather than where it is now; fixtures/README.md carries the re-derived set. The agent had the skill and the directory and nothing else: not the expected answer, not this file. It followed the rules that say how to rank a finding and reshaped the ones that say how to present it: the verdict runs to three sentences where SKILL.md asks for one, and the aligned columns became tables. That gap is recorded rather than papered over in fixtures/README.md, together with what every calibration run scored across all four fixtures.

The five interface axes

Surface (A1 through A14) is the visual layer: the palette, the radius, the shadows, the type scale, the icons and motion. Fourteen tells, three of them (A1, A3, A5) absences that live in the theme file rather than in any one component.

Craft (C1 through C16) is whether anyone looked at the rendered result: a radius that ignores what it wraps, a counter that jitters instead of holding still, a heading that leaves one word behind, a dark theme nobody opened. Sixteen tells read relationships between elements rather than any single line. They compare a value with its container and the same screen across themes.

States (S1 through S3) is whether the interface exists off the path that was demonstrated: a request with no failure branch, view state the URL never learns, an action that cannot be taken back or stopped. Three tells whose evidence is a branch that is missing rather than a value that is wrong.

Words (W1 through W8) is the copy inside the interface: labels, button verbs, empty states, error messages. Eight tells that catch the gap between what a generator writes by default and what a person writes once they have looked at the screen.

Finish (F1 through F13) is thirteen tells covering what a browser or a search engine checks first, things like the lang attribute, the title, the meta tags, the favicon, and a stable key on every mapped list. Nearly all of them are greppable, which is why the audit reads this axis before the other three.

The five text axes

Forty tells on prose, in a separate alphabet so that an id means one thing across the whole plugin.

Hollow (H1 through H9) asks whether anything is behind the sentence: significance nobody measured, a promotional adjective with no fact under it, an authority with no name, a conclusion that would end any text.

Template (T1 through T9) asks whether the content chose the shape or the mould did: three of everything, the bolded header list, a heading that restates itself before starting, one measure for every paragraph. Most of these are invisible while reading and obvious while counting, which is why almost every signal on this axis names a count and what the count decides.

Grain (G1 through G10) asks whether anyone chose the words: the watched vocabulary, the participle tacked onto every second sentence, hedging in layers, a verb buried inside a noun.

Marks (M1 through M6) asks whether anyone set the punctuation. It is the smallest axis on purpose, because a mark is the weakest evidence in the catalog, and every tell on it fires on frequency rather than on one instance.

Presence (P1 through P6) asks whether a person is in the text: chatbot residue, sycophancy, the fake candid opener, prose written against a diff.

The two vocabulary files hold the words. The tells hold the patterns, so a language with no file yet is read by finding the local equivalent.

What this skill refuses to flag

Six patterns look like proof a site was vibecoded. They are not. This skill rejects all six, with a reason:

Not flagged Why
vercel.app URL Free deploy is not a defect
Exposed source maps Error trackers such as Sentry rely on them
No llms.txt A 2024 convention that never became a standard
robots.txt blocking AI crawlers A deliberate choice many authors make
Empty view-source, framework visible in the browser A single-page app is an architecture, not carelessness
Large JS bundle A performance problem, not an authenticity one

Out of scope

A rendered pass, unless the session has browser tooling. The Craft axis asks whether anyone looked and answers it by reading code, which is a limit the catalog carries by construction. Where a browser is available the auditor opens the page at 375, 768 and 1440 in both themes, and marks every finding that came from looking rather than from reading, because a finding stated as observed and one stated as read are worth different amounts to whoever has to reproduce it. The builder runs the same pass as a step of its own, before the floor pass.

A real console error, and running the Finish axis against a site published over HTTP, stay out of scope.

Stack is not one of them. Forty-two of the fifty-four tells never name a framework, a build tool or a library: every tell on States, seven of the eight on Words, fourteen of the sixteen on Craft, eleven of the fourteen on Surface, and seven of the thirteen on Finish. Twelve tells name tools: A1, A2, A7, C14, C16, W8, F1, F2, F5, F8, F11 and F12. They mention Tailwind, React, Next.js, Vite, Storybook or an icon set as examples of a pattern, because that is the ecosystem the pattern was measured in, and the pattern is what fires. A7 names three icon libraries only to say that which one it is makes no difference. Plain HTML and CSS, Vue, Svelte, Astro: read the example, find its equivalent, audit that. A run that translated says so in its first line.

That count has been wrong in this file twice, in both directions. Both errors came from patterns too narrow to catch how the names are written. A search for next.js and react misses next/image, Vite's, Next's and Storybook. Twelve and forty-two is what a proper-noun search returns on 2026-08-22, after the five tells that arrived with the floor. Two of those five carry a name: C16 reaches for outline-none, and W8 releases a Storybook story the way F12 already does, which is what moved Words off a clean sweep.

Also out of scope today, and worth stating because they are the obvious neighbours: performance, SEO beyond what the Finish axis greps, analytics, security and uploads. None of that is here. A skill for the shipping side is designed and not built.

Privacy and legal copy used to sit in that list and no longer does. F13 checks that the pages exist on a public site that collects something, and anti-slop:build carries what they have to contain in legal.md. What is still out is whether the copy is sufficient in any particular jurisdiction, which is a lawyer's call and is said in the handover rather than implied by the page.

How mature each skill is

Not equally, and the difference is worth knowing before you rely on one.

anti-slop:audit is the mature half. Six calibration rounds produced twenty-five blind audit reports committed under calibration/. Twenty cover the auditor reading fixtures. Three cover the auditor reading what the build skill produced. Two cover the auditor reading this project's own site and specimen. Counting every blind report rather than every audit, thirty are committed there: the twenty-five above, the fixer's single run and the rewriter's four. site/inventory.md carries the command behind each figure. Its most recent scores are below.

anti-slop:text is at 0.2.0 and has one round behind it. Its forty tells, its four specimens and its expectation rows exist, and the first blind round scored them on 24 August 2026: four runs, one per specimen, recorded in calibration/2026-08-24b/README.md. slop-release-en carried 22 of its 23 expected ids and nothing off row, clean-release-en 0 of 20 forbidden, slop-notice-pt 15 of 16 expected plus one off row fire, and clean-notice-pt 0 of 15 forbidden. Both clean specimens came back byte identical and neither rewrite invented a fact. The round also found more than it settled, M1's undefined denominator first among it, and the record rather than this paragraph is where that lives. The rows in corpus/README.md still say only what each specimen was written to carry, which is a claim about the specimen and not a measurement of the catalog.

One number in it was measured before that round, and only against the four documents this repository wrote: M1's dash threshold. It is worth knowing what that measurement did, because it is the whole method in one afternoon. The tell shipped counting dashes per word. Counting the specimens showed that rate separating nothing: both clean specimens use one paired interruption on purpose, and both landed above the threshold, because a pair is two characters and a short document is short. The measure that separates is the share of a text's clause joints the dash carries. The tell, both vocabulary files and two of the specimens changed the same day. That is a floor found by counting, not a rate from the wild, and it says so where it sits. The blind round then found that M1's Signal does not define its own denominator, so no two readings of it agree and three of the four published figures reproduce under neither recount. The threshold did not move and the record carries a proposed definition. Read it before trusting this paragraph.

One tell, P5, is flagged as the first candidate for removal, because it is the only one that fires on an absence of opinion and so the only one that can push a rewrite into inventing a position. Use the skill, and read what it hands back before you ship it.

anti-slop:build is at 0.4.0 and younger than the auditor. Three specimens have been built with it and all three audited blind, by agents that had not seen the build skill: 2 of 41 tells fired on the first, 0 of 41 on the second, 0 of 41 on the third. Forty-one is the size the catalog was at the time, not its size now. The two that fired on the first traced to reference entries that did not exist at the time and now do. Fourteen repairs came out of the second and third builds and are not yet measured. BACKLOG.md lists seven gaps around application screens. The skill was written with pages in mind and remains thinnest on screens with real state.

anti-slop:fix is at 0.1.0 and has exactly one round behind it. It shipped this round with repairs.md mapping all fifty-four interface tells to the rule that repairs each one, in five classes: derive 13, declare 10, branch 10, write 17 and unsettled 4. The loop was then run blind on a copy of fixtures/slop-dashboard, and the record is calibration/2026-08-24/README.md: 29 findings fired before, 16 after, 13 closed and 0 opened. 15 were repaired and 14 refused, of which 12 refusals are correct. Three of the fifteen repairs did not close the finding they attacked, and five changed the row rather than the cause. Nothing else this round changed has been measured. BACKLOG.md records what is owed.

All four are usable. The auditor is the one with the evidence trail, the builder has three measured specimens, the fixer has a repair map and one measured pass over one fixture, and the rewriter has one blind round.

Testing & Reliability

A prose catalog cannot be unit tested for whether its judgements are right. What it can be tested for is structural integrity, and that is what runs here.

python scripts/validate.py parses both catalogs and checks that every tell carries all four required fields, that no id is defined twice, that the frontmatter keys and description triggers are present, and that every id named by a specimen actually exists in that skill's own catalog. The last clause is the one the third skill made necessary: the two catalogs share no ids, so they are checked separately, and a text id in an interface row is reported rather than quietly accepted.

It currently reports 0 problem(s), along with four coverage notes: 20 of 54 interface tells appear in no fixture row and 26 have no forbid row, and 13 of 40 text tells appear in no corpus row and 18 have no forbid row. Those are printed rather than suppressed, so the gaps in calibration are visible instead of implied.

python -m pytest tests/ runs 72 tests over the validator itself, all passing. The validator is written so that every check takes text and returns a list of errors, with only main() touching the filesystem, which is what makes those tests possible without fixtures on disk.

What the blind runs scored

A prose catalog is calibrated by handing it to an agent that has not seen the answer. Each run gets SKILL.md, the reference files its invocation names and one target directory. It never receives fixtures/README.md, another fixture or a previous report. docs/calibration-method.md is the method, including the one instruction that turns an audit into a calibration: say which rules you had to supply that the tell does not contain. That disclosure is where most of the catalog's repairs have come from.

The most recent round, 2026-08-17:

Target Result
slop-dashboard, Surface 6 of the 6 expected ids, nothing off-row
slop-dashboard, Craft 10 of the 10 expected ids, nothing off-row
clean-dashboard, Craft 0 of 19 forbidden ids leaked, no finding of any kind

The reports are in calibration/, unedited, including the ones that contradict the catalog. fixtures/README.md records what every round scored before the repairs it caused. It also carries a Recorded for … not fixed section for work that was found and deliberately left. That is currently the longest part of the file.

The corpus

The four specimens under fixtures/ are the closest thing to an accuracy test: a dashboard and a landing page, each built twice, once as work nobody finished and once as work somebody decided. Each carries an expect row of ids it was built to trigger and a forbid row of ids that must not fire. A tell that fires on a clean fixture is a false positive with a name and a location, which is how the "Not slop when" clauses get their pressure. fixtures/README.md records what every calibration run scored, including where the agent departed from the procedure.

The four specimens under corpus/ do the same job for prose, with one difference forced by the skill returning text rather than findings: a run is scored by reading its rewrite against the row, not by reading its report. Every id on an expect row should be gone from the output, every id on a forbid row untouched in it. clean-notice-pt is the sharp one. It is passive three times where the court and not the writer is the actor, it opens and closes with the fixed forms of formal correspondence, it takes no position anywhere, and it carries a dash used once as a real aposto. Every one of those is a pattern this catalog fires on, and every one of them is correct in that document. A rewrite that flattens them has destroyed a working notice to make it friendlier.

pytest is the repository's only dependency.

Contributing

A new tell needs all four fields (Signal, Principle, Fix, Not slop when) and a free id. Two commands have to pass before you open a pull request:

python scripts/validate.py
python -m pytest tests/

The first checks that every tell has its four fields, that no id is defined twice, and that every id a specimen names exists; it has to print 0 problem(s). The second covers the validator itself and needs pytest, the only dependency in the repo; install it with pip install pytest. As a convention the validator does not enforce, also add the new id to whichever specimen row already demonstrates it: fixtures/README.md for an interface tell, corpus/README.md for a text one.

The two catalogs use disjoint letters. Interface tells are A, C, S, W and F; text tells are H, T, G, M and P. A new axis needs a free letter added to TELL_LETTERS in scripts/validate.py, and a new skill needs an entry in the SKILLS registry beside it, naming its description triggers, which of its references carry prose rather than tells, and where its expectations live.

License

MIT. See LICENSE.

About

Audit an interface for the marks of work nobody finished. A Claude Code plugin: 49 tells across surface, craft, states, words and finish, each with the case for when it does not apply.

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages