Skip to content

feat(ai-slop): unslop-inspired catalog additions, plus the corrections and dogfood config that make them true (0.2.0) - #3031

Merged
kyle-sexton merged 6 commits into
mainfrom
claude/ai-slop-unslop-integration-l8gr0k
Aug 19, 2026
Merged

feat(ai-slop): unslop-inspired catalog additions, plus the corrections and dogfood config that make them true (0.2.0)#3031
kyle-sexton merged 6 commits into
mainfrom
claude/ai-slop-unslop-integration-l8gr0k

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

No linked issue

Summary

Adds a set of AI-writing tells inspired by Cursor's unslop skill to the ai-slop catalog, then corrects every claim the plugin made about itself that the shipped code did not actually perform. Also makes the plugin dogfoodable in this marketplace for the first time.

Three commits, deliberately kept separate: the first adds the rules, the second corrects what the first over-claimed, the third prunes the task branch's contract slice after graduating its deferred items. The second exists because an exploration and research pass over the first found that its detection half was sound and its documentation, registration, and dogfooding halves were not.

Fix

New detection (commit 1). Three script rules — rule-chatbot-artifacts (chat-turn residue and sycophancy; IMPORTANT in the severity crosswalk), rule-filler-phrases, rule-stacked-hedging (both SUGGESTION) — each with an argued crosswalk row. Four new rubric tells: false ranges, colon crutches, abstract metaphor jargon, mechanism-free claims. rule-abstract-metaphor-jargon was deliberately kept out of the script layer on measurement: "substrate" alone had 103 legitimate technical uses in this corpus. New reference/rewrite-guide.md owns fix-time guidance, including the guardrail that an em dash must not become a parenthesis or en dash (swapping one tell for another is not a fix).

Corrections (commit 2). Four places a document claimed coverage the code does not provide:

  • The overlap map said "already covered by"; four of its rows point at recorded-only entries that run in neither layer. Header now says "catalogued by", and those rows say they are dormant.
  • Name-dropping was mapped to rule-vague-attribution, which describes the opposite tell (naming no source, versus naming many with no content). Now recorded as deliberately out of scope for general prose.
  • Generic conclusions named a rubric fallback that cannot reach a bare optimism closer. Now recorded as detected only in its formulaic half.
  • The rule-inline-header-lists boundary refinement was attached to a recorded-only entry while the changelog advertised it as live. Now labelled calibration pre-work.

Drift guards. All 15 rules' emitted tiers are asserted (was 2 of 15), plus a roster-set check so a rule added to detect.sh without a crosswalk row can no longer emit SUGGESTION by silent fall-through. The tier: frontmatter field and --tier flag are retired from emit-findings.sh — both owner docs already said this producer omits it, and the flag defaulted to a hardcoded value describing no property of the run.

Dogfooding. .claude/ai-slop.json disables rule-em-dash with a recorded reason: this marketplace's house style uses em dashes deliberately, measured at 34,999 matching lines across 1229 tracked files. That is a style choice, not a defect backlog. The shipped default stays zero-tolerance.

Registration. ai-slop added to the config-cascade Implementers table. The surface implemented the full three-layer cascade from its first release and was never tabled, so that table under-reported a conforming surface.

Attribution. The upstream inspiration is a single link. The Wikipedia CC BY-SA attribution is unchanged, being a different licence with its own share-alike terms.

Verification

  • plugins/ai-slop/skills/audit/scripts/detect.test.sh84 cases pass, up from 65.
  • scripts/check-detector-findings-crosswalk.sh --check — exit 0, 22 rule rows, every disposition argued from a stated test.
  • scripts/check-contract-slice-prune.sh --check-diff origin/main — exit 0 after commit 3.
  • shellcheck + shfmt -d clean on all three scripts; markdownlint-cli2 0 issues across 11 touched files; typos clean; jq validates both changed JSON files.
  • Repo-wide detector run: 173 findings across 1212 files scanned, 18 declined. Before the config, the same run produced roughly 34,900 rule-em-dash findings alone.
  • The three new rules fire on real content without flooding: filler 20, chat residue 5, stacked hedging 2.

Two findings surfaced by verification rather than by review, both fixed here:

  1. Test config isolation. The suite resolved its config cascade from the consuming repo, so adding the dogfood config turned nine unrelated cases red, each reporting disabled=1 for a rule the case never mentions. Fixtures now pin HOME and CLAUDE_PROJECT_DIR to empty directories, the same isolation the suite already applied to inherited git state.
  2. Overlap-map completeness was verified mechanically, not by reading: a script derived a distinctive token per upstream pattern and grepped the catalog and guide for it. All 31 upstream patterns plus both non-numbered sections are accounted for; zero unaccounted.

Related

Both were graduated out of this branch's contract slice, which commit 3 prunes per the topic-docs convention.

claude added 2 commits August 19, 2026 05:42
Adapt the unslop skill from cursor/plugins pstack (MIT, pinned at 99559f2)
into the ai-slop audit skill, deduplicated against the Wikipedia-derived
inventory in a catalog overlap map.

- Catalog: second attribution and drift record, 7 new entries (3 script,
  4 rubric), inline-header-lists boundary refinement, second calibration
  pass recorded.
- Detector: rule-chatbot-artifacts, rule-filler-phrases,
  rule-stacked-hedging pattern rules; plain-word trio joins the
  vocabulary default. Tests extended to 65 cases.
- Crosswalk: three argued rows (chatbot-artifacts IMPORTANT, the other
  two SUGGESTION); adopter tier-spread counts updated; convention
  CHANGELOG 2.3.0; emitter tier/action mirror updated.
- New reference/rewrite-guide.md owns fix-time guidance (plain speech,
  substitution guardrails, voice, self-audit); fix flow reads it first.
- Version 0.2.0; README, CHANGELOG, plugin.json, docs listings synced.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AqwX2njhiLMWGrMg3QNt4g
Follow-up folded into the same unreviewed branch rather than shipping
0.2.0 with a changelog advertising an inert feature. Version stays 0.2.0.

Attribution: the Cursor upstream is now a single "inspired by" link. The
licence apparatus and the commit-pinned second drift record are removed;
the Wikipedia CC BY-SA block is untouched, being a different licence with
its own share-alike terms.

Honesty defects — four places a doc claimed coverage the code does not
provide:
- Overlap map header says "catalogued by", not "covered by", and names
  the rows whose entries are recorded-only and therefore dormant.
- Name-dropping recorded as deliberately out of scope for general prose;
  it had been mapped to a rule describing the opposite tell.
- Generic conclusions recorded as detected only in the formulaic half;
  the named rubric fallback cannot reach a bare optimism closer.
- inline-header-lists refinement labelled calibration pre-work, not a
  live boundary.

Drift guards:
- All 15 rules' emitted tiers asserted (was 2), plus a roster-set check
  so a rule added without a crosswalk row cannot emit SUGGESTION by
  silent fall-through.
- tier: frontmatter and --tier retired from emit-findings.sh; both owner
  docs already said this producer omits it.
- Test config isolation: fixtures pin HOME and CLAUDE_PROJECT_DIR to
  empty dirs. Found by dogfooding, when this repo's own house-style
  exemption turned nine unrelated cases red.

Dogfooding: .claude/ai-slop.json disables rule-em-dash with a recorded
reason. First repo-wide run drops ~34,900 findings to 173 across 1212
files, making the audit usable here for the first time.

Also: ai-slop registered in the config-cascade Implementers table (the
surface implemented the cascade from its first release and was never
tabled); relay expectations narrowed to rule-utm-params; word-list
narrowings that were silent are now recorded.

Suite: 84 cases pass, up from 65.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AqwX2njhiLMWGrMg3QNt4g
@claude

claude Bot commented Aug 19, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 30s —— View job


Security review (/review:security-review)

Result: No security issues found

Reviewed the full diff (git diff b9de992...8e7731a, 18 files, +581/-60 — matches the PR's stated stats) against the security-review criteria (injection surfaces, auth/access-control, secret handling, dangerous Actions patterns, permission widening, supply-chain risk).

The change set is entirely:

  • Markdown documentation (catalog, README, CHANGELOG, PLAN, convention docs)
  • Config/manifest JSON (.claude/ai-slop.json, plugin.json, marketplace.json) — plain string/array values consumed via jq -r and used only as shell glob patterns in case statements, never eval'd or interpolated into commands
  • Shell script changes in detect.sh (new hardcoded regex rules only), emit-findings.sh (removes an unused --tier flag), and detect.test.sh (new fixtures + test isolation fix that pins HOME/CLAUDE_PROJECT_DIR, reducing environment coupling rather than introducing any)

No new command execution, network calls, credential/secret handling, auth/access-control logic, or GitHub Actions workflow changes are introduced. Grep for curl|wget|eval|base64|secret|token|password|api_key|ssh-|BEGIN PRIVATE across the diff turned up only benign substring collisions (e.g. "curly quotes"). All grep/awk invocations that consume rule expressions use hardcoded string literals and -- argument separation consistent with the pre-existing pattern; no user- or config-controlled data reaches an eval/bash -c/command-substitution sink.

Per the skip gate, this PR has no security-relevant surface — reporting no findings and stopping here.

docs/topics/<slug>/ is contract tier per the topic-docs convention:
committed on a task branch, pruned before merge once its durable
outcomes have graduated. Both deferred questions are now tracker items:

- #3032 — whether any shipped rule appears under Wikipedia's
  "Ineffective indicators", which is a guardrail question on
  rule-em-dash and rule-rule-of-three. Blocked here by the research
  environment's Wikimedia egress denial, not by scope.
- #3033 — letting a detector-findings producer name the remediation
  skill its findings need. The real fix for the relay gap this PR only
  narrowed the claim about.

The Brief's constraints and acceptance criteria are carried by the PR
body; the interview ledger stays in the gitignored memory slice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AqwX2njhiLMWGrMg3QNt4g
@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown

Last security-reviewed head: ba121a397e9c50089a78375a3f5768bd84cb2dcc. On the next push, the relevance gate compares only the commits since this SHA; delete this comment to force a full re-review.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8e7731a233

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread plugins/ai-slop/skills/audit/scripts/detect.sh Outdated
claude added 2 commits August 19, 2026 17:18
The changelog-parity gate requires `## [<version>]`; both entries were
written unbracketed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AqwX2njhiLMWGrMg3QNt4g
@claude

claude Bot commented Aug 19, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 5m 34s —— View job


Claude code review (/review:code-review)

  • Gather context (PR body, changed files, CLAUDE.md, REVIEW.md)
  • Run /review:code-review criteria against the diff
  • Fan out review of changed files (script logic, docs/config consistency)
  • Cross-check self-consistency of claims across code and docs
  • Finalize summary comment

Summary

No blocking or high-signal correctness findings. This PR's own verification claims mostly held up under independent static cross-checking:

  • Rule roster / tier consistency: emit-findings.sh's rule_tier() (3 IMPORTANT: rule-knowledge-cutoff-disclaimer, rule-llm-citation-artifacts, rule-chatbot-artifacts; 12 SUGGESTION) exactly matches the crosswalk table in docs/conventions/detector-findings/README.md and the detect.test.sh EXPECTED_IMPORTANT/EXPECTED_SUGGESTION sets. detector-findings/README.md#L231-L233, emit-findings.sh#L112-L117.
  • New detector regexes (rule-chatbot-artifacts, rule-filler-phrases, rule-stacked-hedging in detect.sh:55-57) correctly fire on their positive fixture and correctly stay quiet on the "benign neighbor" negative fixture (e.g. feel free to (ask|reach out) deliberately excludes the benign "feel free to customize"; in order to doesn't false-positive on "ascending order to").
  • Catalog inventory counts check out exactly: 66 ### rule- entries (59 Wikipedia + 7 Cursor, as claimed), and the crosswalk table has exactly 15 ai-slop/audit/rule-* rows.
  • Retired --tier flag: no remaining callers pass --tier to emit-findings.sh, and both docs that reference the omission (context/persist-findings.md, this PR's changes) agree.
  • Test count claim: manually tallied every pass/fail-producing statement in detect.test.sh (including the two dynamic loops over 3+12 rules) — comes to exactly 84, matching the PR's "84 cases, up from 65."
  • Config/JSON validity: .claude/ai-slop.json, plugin.json, and marketplace.json are all valid JSON; the _comment measurement of "1229 tracked markdown files" matches git ls-files '*.md' | wc -l exactly.

Minor, non-blocking observation (not filed as a finding): rule-stacked-hedging's ERE (detect.sh:57) covers 5 of the 6 possible hedge-pair combinations ("may possibly" is not matched, matching the catalog entry's own enumerated list, so this looks like a deliberate scope choice rather than an oversight).

Verification note

This sandbox's Bash tool required interactive approval for executing the test suite / shellcheck / shfmt / markdownlint-cli2 directly, which isn't available in this non-interactive lane, so I could not independently re-run them. Everything above was verified by reading the shipped code/docs and cross-referencing counts and identifiers directly rather than trusting the PR body's stated results. The PR body's shellcheck/shfmt/markdownlint-cli2/typos/repo-wide-scan output numbers are author-claimed and unverified by me.
· branch claude/ai-slop-unslop-integration-l8gr0k

@claude

claude Bot commented Aug 19, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 0s —— View job


Security review (/review:security-review)

Result: No security issues found

This PR was already security-reviewed at head 8e7731a (no issues found). Per the skill's relevance gate, I diffed 8e7731a..5a8349d to see what changed since:

Neither of the two PR-relevant commits touches code, config, or CI — both are documentation-only (a changelog heading fix and a planning-doc deletion). No new injection surfaces, auth/access-control logic, secret handling, or GitHub Actions changes were introduced. Per the skip gate, there is no security-relevant surface in the delta since the last review — reporting no findings and stopping here.

@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown

Claude has reviewed this PR 2 times. The lane skips further automatic reviews after 5; deleting this comment resets the count.

Reported in review and reproduced before fixing: "These are great questions
for the reviewer" fired rule-chatbot-artifacts, which emits at IMPORTANT
tier, because the listed phrase merely prefixed a longer word. Same class
hit "found the smoking guns".

The registry gains a per-rule whole-word flag using POSIX -w rather than
GNU \b, since this script's cross-grep parity claim rests on POSIX ERE
only. It is ON for the five phrase-list rules and OFF where a match
legitimately abuts a word character: the byte-class rules (em dash, emoji,
curly quotes), the two EREs carrying wildcards, the citation tokens
(`[cite:` is followed by digits), and `utm_[a-z]+=` (followed by its value).

Both reported cases and their true-positive counterparts are now fixtures,
so a rule silenced into uselessness fails as loudly as one that over-fires.
Suite: 86 cases pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AqwX2njhiLMWGrMg3QNt4g
@claude

claude Bot commented Aug 19, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 50s —— View job


Security review (/review:security-review)

Result: No security issues found

This PR was already security-reviewed at heads 8e7731a and 5a8349d (no issues found either time). Per the skill's relevance gate, I diffed 5a8349d..ba121a3 to see what changed since the last review:

  • ba121a3fix(ai-slop): match phrase rules on whole words. Adds a per-rule whole-word flag to the PATTERN_RULES registry and threads it through to a POSIX grep -E -w flag (detect.sh#L421-L430), fixing the exact false-positive Codex flagged on this PR (rule-chatbot-artifacts matching "great question" inside "great questions"). Plus matching detect.test.sh fixtures/assertions and a CHANGELOG.md entry.

This is a pure bug-fix to hardcoded regex/flag logic:

  • The new word field comes only from the hardcoded PATTERN_RULES array literals, not from any external or config-controlled input — no injection surface.
  • flags+=(-w) is appended to a grep invocation as an array element, never string-interpolated or eval'd — consistent with the existing -i/-E handling in the same function.
  • Test-only additions (detect.test.sh) exercise the fixture files already covered by prior reviews; no new sinks introduced.

No new command execution, network calls, credential/secret handling, or auth/access-control logic. Per the skip gate, there is no security-relevant surface in the delta since the last review — reporting no findings and stopping here.

Note: the local checkout also shows an uncommitted working-tree change to .claude/ai-slop.json (removes disabled_rules/_comment, adds a fixtures/** exclusion path). It is not part of this PR's committed history (not in git diff origin/main...HEAD), so it is outside this review's scope — flagging only so it isn't mistaken for a reviewed change.

@claude

claude Bot commented Aug 19, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 5m 23s —— View job


Code review (/review:code-review)

Summary

Reviewed the full diff against origin/main (17 files, +547/-72 — matches the PR's stated stats), including the latest commit (ba121a3) pushed since the last review pass, which fixes the Codex P2 comment on detect.sh's unbounded phrase matching.

Verified: the Codex boundary-match fix is correct and well-tested. detect.sh:39-56 adds a per-rule whole-word flag (POSIX -w, deliberately not GNU \b, preserving the cross-grep parity claim) and turns it on for every phrase-shaped rule while correctly leaving it off for the byte-class rules and the two EREs carrying [^.]{0,80} wildcards (where -w would be meaningless/wrong). detect.test.sh gained both a negative fixture ("great questions", "smoking guns") and a positive fixture confirming the exact phrases still fire — good adversarial coverage, not just a silence check.

One finding posted inline: catalog.md:74-76 — the shipped catalog and CHANGELOG both say rule-abstract-metaphor-jargon's calibration measured "substrate" at 114 legitimate uses, but the PR description's Summary states 103. The two shipped docs agree with each other, so the PR body is the outlier here — worth reconciling given this PR's stated purpose is making every claim about the plugin true.

Static cross-checks that held up (not restating the PR's own verification claims — independently checked by reading the diff):

  • emit-findings.sh's rule_tier() (lines 112-118) exactly matches the 3-IMPORTANT/12-SUGGESTION crosswalk rows added to detector-findings/README.md and the EXPECTED_IMPORTANT/EXPECTED_SUGGESTION sets in detect.test.sh.
  • Rule roster is exactly 15 (12 PATTERN_RULES + 3 density rules), matching the "15 rules" claim throughout.
  • Catalog inventory is exactly 66 ### rule- entries (59 Wikipedia + 7 Cursor), matching the prior review's count.
  • Retired --tier flag: no remaining callers; emit-findings.sh's frontmatter output and usage text are consistent with the removal.
  • .claude/ai-slop.json, plugin.json, marketplace.json are valid JSON; git ls-files '*.md' | wc -l = 1229, matching the dogfood config's _comment.

Not independently verified (no Bash execution permission in this lane): the test suite's exact "84 cases, up from 65" count (the last commit added 2 more assert_contains cases since that figure was written, so it may now be marginally stale — cosmetic, not filed as a finding), shellcheck/shfmt/markdownlint-cli2/typos output, and the repo-wide detector run numbers. These remain author-claimed per the PR body.

No blocking correctness or maintainability issues found otherwise. Security is out of scope for this lane (already covered, no findings, by the dedicated security-review passes on this PR).
· branch claude/ai-slop-unslop-integration-l8gr0k

@kyle-sexton
kyle-sexton merged commit dcd51a5 into main Aug 19, 2026
48 checks passed
@kyle-sexton
kyle-sexton deleted the claude/ai-slop-unslop-integration-l8gr0k branch August 19, 2026 17:43
Comment on lines +74 to +76
noise).
- `rule-abstract-metaphor-jargon` stays rubric, not script, on measurement: "substrate" alone hit
114 times in legitimate technical use on this corpus. A word-list scan cannot make the

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Measurement mismatch: this file says 114, the PR description says 103.

rule-abstract-metaphor-jargon stays rubric, not script, on measurement: "substrate" alone hit 114 times in legitimate technical use on this corpus.

The PR body's Summary states: "rule-abstract-metaphor-jargon was deliberately kept out of the script layer on measurement: 'substrate' alone had 103 legitimate technical uses in this corpus."

plugins/ai-slop/CHANGELOG.md's new [0.2.0] entry agrees with this file (114), so the two shipped artifacts are internally consistent with each other — only the PR description disagrees, and it has said 103 since the PR was opened (unchanged across all three commits and two prior review passes). A rough independent count (git ls-files '*.md' | xargs grep -ilw substrate, whole-word case-insensitive, tracked markdown) turns up 34 files, so neither number is obviously implausible without knowing the exact corpus scope/date the calibration pass used — but given this PR's entire second commit exists to make the plugin's claims about itself match reality, a number that disagrees with itself across the PR body vs. the shipped doc is worth reconciling (or explaining the source of the discrepancy) before merge.

Fix this →

kyle-sexton added a commit that referenced this pull request Aug 19, 2026
…reach (0.2.1) (#3039)

No linked issue

## Summary

`ai-slop` ships two detection layers. The deterministic one has 86 shell
cases covering all 15 script rules, tiers, roster agreement, exemptions,
and portability. The judgment one — 11 rubric tells, plus the
`rewrite-guide.md` the fix flow reads at step 1 — had **zero** coverage.
That is the half only a model performs, so it is the half evals exist
for.

This adds 7 eval cases (audit 4 → 9, setup 3 → 5), bringing `ai-slop` in
line with sibling audit skills (7, 9, and 14).

## Fix

**`audit`, five new cases**, each tied to a documented behavior that no
shell test can assert:

- **`rubric-findings-reach-the-report-not-the-findings-file`** — the V1
relay boundary. A rubric verdict has no crosswalk row to look a tier up
from, so it reaches the human report only. Silently dropping it from the
file and silently including it are both failures, and only a model can
tell them apart.
- **`fix-never-swaps-one-tell-for-another`** — the substitution
guardrail. An em dash must resolve to a comma, a period, or a
restructured sentence, never a parenthesis or en dash. This is the
guardrail most likely to be violated silently, because the swap *looks*
like a fix.
- **`triads-collapse-toward-the-strongest-item`** — a `rule-of-three`
fix collapses toward one item rather than being repunctuated into a
three-item list, and keeps a triad whose elements are each load-bearing,
saying why.
- **`known-false-positive-routes-to-config-not-a-rewrite`** — prose
*about* model knowledge cutoffs trips
`rule-knowledge-cutoff-disclaimer`. The catalog's calibration record
already names this class; the recorded answer is a marker or config
exclusion with a stated reason, never weakening the shipped rule to make
one corpus pass.
- **`declined-exemptions-are-named-with-their-cause`** — a disabled rule
and an excluded path must read as decisions, not as an absence of
findings.

**`setup`, two new cases** covering the `_comment` rationale key
introduced in 0.2.0: it is a documented annotation rather than
unknown-key drift (while genuinely unknown keys still flag), and
disabling a rule records *why* alongside the trade-off against
`em_dash_allowed_paths`.

**Every case now carries `narration: true`.** The four original cases
named `docs/example.md`, which resolves nowhere — a live skill-quality
Q4 warning that the flag exists to answer.

## Verification

- `check-jsonschema` against
`plugins/skill-quality/reference/evals.schema.json` — both files `ok`.
- `scripts/check-changelog-parity.sh --check` and `--check-bump
origin/main` — both pass at 0.2.1.
- `plugins/ai-slop/skills/audit/scripts/detect.test.sh` — 86 cases pass,
unchanged (this PR adds no rules).
- `node scripts/generate-catalog.mjs` — catalog already in sync.
- `markdownlint-cli2` and `typos` clean on the touched files.

**Dogfood context that motivated the selection.** A repo-wide run on
`main` now reports 174 findings across 1214 files, and sampling them
shows the plugin predicting its own false-positive profile: 16 of 21
`rule-emoji-formatting` findings are ❌/✅ semantic wrong-right markers (a
documentation convention here, not decoration), 8
`rule-knowledge-cutoff-disclaimer` findings are the class the
calibration record already names, and 72 `rule-curly-artifacts` findings
concentrate in just 7 files as genuine paste residue. Two of the new
evals encode exactly the judgment those findings require.

## Related

- Refs #3031 — the 0.2.0 integration this covers the judgment half of.
- The de-slop pass over those 174 findings is deliberately NOT in this
PR: a large share are the repo's own conventions, so the next step there
is config tuning rather than editing prose, and it touches ~100
unrelated documents.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01AqwX2njhiLMWGrMg3QNt4g)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
kyle-sexton added a commit that referenced this pull request Aug 21, 2026
… skill (0.26.0) (#3068)

Closes #3033

## Summary

The `detector-findings` convention lets a producer emit a findings file
that `review:fanout`'s `fix`
action consumes. The relay then decided **how** to remediate — and for
`ai-slop:audit` that decision
was wrong for 14 of its 15 rules.

Those rows are prose-style findings. They classify as **cleanup** by
content, and `fix-pass-mode.md`
Step 4 hands the whole cleanup class to `/simplify` — a
**code**-simplification skill that, in its
own words, "rediscovers cleanups from the working-tree diff — it does
NOT read the findings files",
and which never loads
`plugins/ai-slop/skills/audit/reference/rewrite-guide.md` where that
plugin's
rewrite discipline lives. Step 5 then retired the file anyway. A relay
pass over an `ai-slop:audit`
findings file reported a clean run while applying at most
`rule-utm-params`, the one genuinely
auto-applicable rule. #3031 narrowed the plugin's own claim to say
exactly that, which was honest and
left the gap.

This teaches the contract to let a producer name the skill that owns its
findings' remediation, and
teaches the relay to honor it — **without a new field, and without any
producer changing what it
emits.**

### The question settled first: does an existing disposition already
cover this?

Asked before anything was written, because a third disposition an
existing one already covers is
pure cost. **It does not**, on two independent grounds:

1. **Off-site is a statement about the SITE, and these repairs are at
`Location`.**
`docs/conventions/detector-findings/README.md` "When the remediation is
not at `Location`" binds
its producer obligation to "a rule whose remediation can lie **outside
`Location`'s file**", and
both limbs of the consumer's trigger in `fix-pass-mode.md` "Step 2" are
site limbs — the `Action`
names a different file, or the contract declares the rule off-site.
`testing:audit`'s own adopter
row states the contrast: its `Location` "IS the remediation site".
Claiming off-site to reach the
disposition would assert something false about where the fix goes, the
same defect the doc already
forbids in retargeting `Location`. And it routes to **surface-only**,
which trades a wrong apply
   for no apply rather than closing the gap.

2. **`Auto-applicable: No` has no path to the route that actually
misapplies these rows.** Step 4's
surface-instead-of-auto-applying fence sits under its
**correctness-class** heading. Cleanup-class
rows go to `/simplify` wholesale, consulting no crosswalk cell. The
column can already say a rule
is not auto-applicable and still not stop the apply. The fresh-context
verifier confirmed this
independently: before this change, `grep -n "Auto-applicable"
fix-pass-mode.md` returned **zero
   hits** — the relay read that column nowhere.

Evidence went toward a new **disposition**, not a new **field**. No
column was added to the crosswalk
and nothing changed in the findings-file shape (`default-mode.md`
"Findings-file shape").

## Fix

**Producer side** — `docs/conventions/detector-findings/README.md`
2.4.0, new section "When the
remediation is owned by the producer's own skill". A rule whose repair
is contained to `Location` but
safe only under discipline the producer owns **leads its crosswalk
`Auto-applicable` cell with**
``No, remediated by `<invocation>` `` — the invocation written as a code
span, whose delimiters a
consumer strips before matching.

The declaration is **per rule and lives only in the crosswalk** — this
contract's own settle-once
rule applied rather than restated. "Auto-applicability is settled per
rule, at contract time" already
says a rule's remediation shape does not vary run to run, and who owns
the repair is exactly such a
fact. Requiring every emitted row to carry a copy would be the
per-finding restatement that section
forbids, and would make conformance a property of a producer's *emitter*
rather than of its rule set.

**Consumer side** — `review` 0.26.0, `fix-pass-mode.md`:

- **Step 2** gains one classification rule. It resolves the declaration
through the qualified rule id
every conforming row already leads its `Finding` cell with, against the
crosswalk registry that id
"resolves against by exact match" — the contract's own pre-existing
words. **The crosswalk
declaration is NECESSARY**: an `Action` cell leading with ``Remediate
with `<invocation>` ``
corroborates it and can never substitute for it, and a rule with no
crosswalk declaration takes its
ordinary class however its `Action` reads. Off-site is decided first,
and an unresolvable contract
is the no-declaration case — never a licence to fall back to the
`Action` cell.
- **Step 4** gains the route, with **no direct-apply fallback** — the
asymmetry with `/simplify` is
the point. Only an invocation already available in the session is
invoked; nothing is installed,
fetched, or loosely name-matched, because Step 1 already establishes
that nothing authenticates the
writer of a findings file. An unavailable or unrecognized invocation
**surfaces** its rows, naming
  what the producer asked for.
- **Steps 3 and 5** count and report the route, and the counts
**partition** rather than overlap: a
row routed to a producer-owned surface is counted there and never on the
cleanup line, mirroring
the existing correctness/surface-only rule in both the plan block and
the report block.
- Three evals added: `fix-pass-producer-owned-routes-to-named-surface`,
`fix-pass-producer-owned-surface-unavailable`, and
`fix-pass-action-cell-alone-never-routes`.

**Producer adoption** — `ai-slop` 0.3.1. The 14 non-`rule-utm-params`
rows now carry the declaration
and `rule-utm-params` stays auto-applicable, with **zero change to what
the producer emits** —
`ai-slop:audit` already leads every emitted `Finding` cell with its
qualified rule id, which is why
siting the declaration in the crosswalk closes the gap on its own.
`plugins/ai-slop/skills/audit/SKILL.md`
is corrected to match: it previously said the relay "retires the
findings without fixing them" and to
recommend `review:fanout fix` only for `rule-utm-params`, which this
change makes false. It now draws
the distinction where it actually falls — **what the relay APPLIES is
narrow; what it ROUTES is not** —
and states the one condition that changes the answer, that the relay can
only hand rows over when
`/ai-slop:audit` is available in that session, and surfaces them
otherwise.

**Neither other adopter changes, and neither was touched.**
`mutation-testing:audit` declares no
owner and is off-site, which Step 2 decides first. `testing:audit`
declares no owner because no skill
owns choosing the assertion a behavior deserves; its rows keep the plain
`No — <reason>` form and are
surfaced by Step 4's judgment fence exactly as before. All three adopter
rows now state their
disposition explicitly.

## Verification

| Gate | Result |
|---|---|
| `check-detector-findings-crosswalk.sh --check` + its self-test | PASS
— 22 rule rows, every disposition argued |
| `check-changelog-parity.sh` `--check` / `--check-bump origin/main` /
`--check-order` / `--check-preserved origin/main` | PASS (83 changelogs
newest-first; 64 headings compared) |
| `check-changed-skills.sh origin/main` | `audit: PASS — 0 errors`,
`fanout: PASS — 0 errors` |
| `check-stale-base-overlap.sh --check origin/main` | up to date with
`origin/main` |
| `check-contract-clause-coverage.py` | PASS |
| `check-contract-slice-prune.sh --check` / `--check-diff origin/main` |
PASS |
| `check-cross-plugin-source-drift.sh --check` | PASS |
| `check-evals-quality.sh` on both changed eval sets | PASS — 0 warnings
|
| `markdownlint-cli2` on every changed markdown file | PASS — 0 issues |
| `detect.test.sh` (regression check) | all 92 cases passed |
| `node scripts/generate-catalog.mjs` | Catalog already in sync;
`docs/CATALOG.md` unchanged |

### Fresh-context verification

Separate opus verifiers, in fresh context and told to judge the final
state rather than any
description of it, reviewed this. The first found **three** real
defects, all fixed: the
`mutation-testing:audit` adopter row misdescribed two of its own
`Auto-applicable` cells; the
declared lead was written as a code span in the shipped cells but bare
in the contract, so a
consumer implementing it literally would match nothing; and the
contract-reachability argument was
evidence about the **producer's** session rather than the consumer's.
Its full verdict is on

[#3033](#3033 (comment)).

### Review findings, all fixed

- **P1 — routing on an `Action` cell alone.** The original text let the
untrusted half of the pair
authorize a route by itself. Since nothing authenticates a findings
file's writer, any component
able to write one could have named any already-installed skill and
handed it arbitrary rows, with
effects bounded neither by `Location` nor by Step 2. Now the crosswalk
declaration is necessary and
the `Action` cell only corroborates — the trust boundary being that the
crosswalk lives in the
consuming repo's own docs, **outside** the artifact being consumed,
while the `Action` cell is
  inside it. **Availability is not authentication.**
- **P2 — ambiguous plan counts.** The cleanup and producer-owned lines
now partition rows explicitly,
in both the plan and the report, so a plan can no longer print
`Cleanup-class (14) → /simplify` for
  rows that never reach `/simplify`.
- **P2 — the producer steering operators off the new route.**
`ai-slop`'s `SKILL.md`, corrected above.

## Related

- #3031 narrowed `ai-slop`'s relay claim to what was then true, and left
this gap open on purpose.
- #3041 / #3063 backed the `ai-slop` audit evals with committed
fixtures; `ai-slop` 0.3.1 here builds
  on the 0.3.0 that shipped there.
- `docs/conventions/detector-findings/README.md` — "The severity
crosswalk", the `Auto-applicable`
column, "When the remediation is not at `Location`" (the precedent this
is argued against, whose
rejected remediation-target column is why no column was added here), and
the Adopters table.
- `plugins/review/skills/fanout/context/fix-pass-mode.md` — the consumer
algorithm this teaches to
  honor the declaration.

### Follow-up, deliberately not in this PR

Two `ai-slop` items are tracked separately and nothing here depends on
either. `emit-findings.sh`
writes `date: …T05-45-10Z` — hyphens where ISO-8601 needs colons in the
time portion; Step 1 classes
that UNREADABLE and fails **open**, so it costs at most one extra pass
and drops nothing. And
`docs/upstream/cursor-pstack.md` carries no `unslop` row, although it
calls itself the single source
of truth for everything derived from that upstream collection and its
recheck trigger only covers
rows its table names.

Merge ordering, disclosed rather than closed: the relay's only contract
URL points at `main`, so the
new crosswalk cells are not visible to a live relay until this merges.

---------

Co-authored-by: Claude <noreply@anthropic.com>
kyle-sexton pushed a commit that referenced this pull request Aug 21, 2026
…ion table

`docs/upstream/cursor-pstack.md` claims to be the single source of truth for
everything in this marketplace derived from cursor/plugins' `pstack/skills/`
collection, and its recheck trigger fires only on a change to a
`pstack/skills/<name>/SKILL.md` **named in the attribution table**. `unslop` is
a member of that collection and `ai-slop` 0.2.0 (#3031) is derived from it, but
the table carried no `unslop` row — so the SSOT claim was false and drift in
`pstack/skills/unslop/SKILL.md` would have gone undetected.

One row, grounded in what is actually in the repo: the seven catalog entries the
Wikipedia inventory did not already carry, the plain-word trio in the shipped
vocabulary default, and `reference/rewrite-guide.md` as the home for the
upstream file's fix-time half; against the overlap map's deduplication, the
Name-dropping and Generic-conclusions exclusions, the calibration measurement
that kept abstract metaphor nouns out of the script layer, and the one
adding-soul bullet that did not survive.

The row says on its face that it was NOT audited at this file's
`main@60c641e4` pin: the verdict was formed at integration time against an
unpinned upstream `main`, and the catalog's own four-part drift record covers
the Wikipedia source page only. The pin is the row's baseline for the next
diff, not a claim about the state it was audited at.

Not one of the port's ten lanes and it does not claim to be — `ai-slop` 0.2.0
landed before this file existed, so "All ten lanes are decided" is unchanged.
Docs-only, outside `plugins/`: no version bump and no plugin CHANGELOG entry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AqwX2njhiLMWGrMg3QNt4g
kyle-sexton added a commit that referenced this pull request Aug 21, 2026
…#3076)

Closes #3032

## Summary

`ai-slop`'s catalog cited Wikipedia's Signs of AI writing but recorded
**Ineffective indicators** (and **Comment-specific indicators**) as a
fetch gap. That left a guardrail question open: if a shipped rule
appeared in the section the page's own editors call unreliable, we would
be shipping a tell our source rejects. A 2026-08-21 retrieval answers
it: **none of the 15 shipped script rules appear there**, so no rule is
dropped or re-scoped.

## Fix

- Closed the fetch gap in
`plugins/ai-slop/skills/audit/reference/catalog.md` against two
revisions of
<https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing>:
- Catalog pin
[1369699198](https://en.wikipedia.org/w/index.php?title=Wikipedia:Signs_of_AI_writing&oldid=1369699198)
(2026-08-16), MediaWiki parse sections 80 / 62 / 29.
- Live page revision
[1370403579](https://en.wikipedia.org/w/index.php?title=Wikipedia:Signs_of_AI_writing&oldid=1370403579)
(`2026-08-20T23:13:41Z`), retrieved 2026-08-21.
- Quoted the eight ineffective indicators (perfect grammar; mixed
registers; "bland"/"robotic" prose; undifferentiated formal prose;
transition words in isolation; unsourced content; bizarre wikitext;
correct wikitext). Same list on both revisions.
- Recorded that `rule-em-dash` and `rule-rule-of-three` remain valid
source-page signs (Style / Language and grammar). The em-dash "in
combination with other indicators" sentence is a corroboration
qualifier, not an Ineffective indicators listing; zero-tolerance stays
the house-style default.
- Comment-specific indicators retrieved in the same visit: Wikipedia
talk-page scope, overlapping existing recorded-only edit-summary
entries. No new slugs.
- Bumped `ai-slop` 0.3.1 → 0.3.2. Detector, emitter, evals, and rewrite
guide are untouched.

## Test plan

- `npx markdownlint-cli2` on the two edited markdown files: 0 issues.
- `python3` parse of `plugin.json`: version `0.3.2`.
- `bash plugins/ai-slop/skills/audit/scripts/detect.test.sh`: all 92
cases passed (roster still exactly 15 script rules).
- `scripts/check-changelog-parity.sh --check`, `--check-bump
origin/main`, `--check-preserved origin/main`: pass.
- `scripts/check-changed-skills.sh origin/main`: PASS, 0 errors.

## Verification

Same commands and outcomes as the Test plan. This is a catalog/docs
close of a fetch gap, not a detector change; the unit suite is the
regression that the 15-rule roster did not move.

## Related

- #3032 — the fetch-gap / guardrail question this closes.
- #3031 — the contract slice this issue graduated from.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
kyle-sexton pushed a commit that referenced this pull request Aug 21, 2026
…ion table

`docs/upstream/cursor-pstack.md` claims to be the single source of truth for
everything in this marketplace derived from cursor/plugins' `pstack/skills/`
collection, and its recheck trigger fires only on a change to a
`pstack/skills/<name>/SKILL.md` **named in the attribution table**. `unslop` is
a member of that collection and `ai-slop` 0.2.0 (#3031) is derived from it, but
the table carried no `unslop` row — so the SSOT claim was false and drift in
`pstack/skills/unslop/SKILL.md` would have gone undetected.

One row, grounded in what is actually in the repo: the seven catalog entries the
Wikipedia inventory did not already carry, the plain-word trio in the shipped
vocabulary default, and `reference/rewrite-guide.md` as the home for the
upstream file's fix-time half; against the overlap map's deduplication, the
Name-dropping and Generic-conclusions exclusions, the calibration measurement
that kept abstract metaphor nouns out of the script layer, and the one
adding-soul bullet that did not survive.

The row says on its face that it was NOT audited at this file's
`main@60c641e4` pin: the verdict was formed at integration time against an
unpinned upstream `main`, and the catalog's own four-part drift record covers
the Wikipedia source page only. The pin is the row's baseline for the next
diff, not a claim about the state it was audited at.

Not one of the port's ten lanes and it does not claim to be — `ai-slop` 0.2.0
landed before this file existed, so "All ten lanes are decided" is unchanged.
Docs-only, outside `plugins/`: no version bump and no plugin CHANGELOG entry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AqwX2njhiLMWGrMg3QNt4g
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.

2 participants