ai-style-impressions

SkillAI & models

Transparent, itemized impressions of AI-generated WRITING STYLE — the repo's ONLY non-integrity track. Two passes: a deterministic defensive-hedge density screen (tools/check_ai_style.py, AIS-DEFENSIVE-HEDGE) plus a fresh cross-model GROSS-cases-only semantic pass over the 13 AIS-* style tells (broken narrative arc, LLM phrase tics, jargon-stuffing, invented codenames, clause/formula walls, gratuitous pseudocode, bullet overuse, bold-module spam, restatement loops, focus drift, single-style figures, appendix dumping). Every finding is named, LOCATED, span-anchored to the evidence ledger (claims.json), carries not_integrity_finding:true + false_positive_risk:high + an fp_case, and gets ZERO verdict weight: the adjudicator forces it to info, excludes it from overall_verdict, and renders it in a SEPARATE report section. NOT an AI-text classifier — no scores, no \"this is AI-written\", no authorship probability; a paper can be CLEAN_GIVEN_EVIDENCE and still list many. Emits ai-style-impressions.findings.json; computes NO verdict; if a tell is actually substantive it routes to the integrity auditor. Triggers: \"AI style\", \"vibe check\", \"writing fingerprint\", \"AI 文风\", \"vibe paper\".

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the ai-style-impressions skill

What this skill tells your AI

The instructions your AI receives, as published by wanshuiyin/anti-autoresearch in skills/ai-style-impressions/SKILL.md and read by ahel’s review.

Surface AI writing-style impressions for: $ARGUMENTS (requires claims.json from /evidence-ledger). Emit span-anchored ai-style-impressions.findings.json. Every finding here is an impression with ZERO verdict weight — this skill proposes no integrity finding and computes no verdict.

⚠️ This is the repo's ONLY non-integrity output, and it is non-integrity by construction. AIS findings are transparent, itemized impressions of AI-generated writing style. The adjudicator (tools/adjudicate_findings.py) gives every AIS finding ZERO verdict weight — it is forced to info, excluded from overall_verdict, and rendered in a separate report section, "AI Writing-Style Impressions — NOT integrity findings · ZERO verdict weight". A paper can be CLEAN_GIVEN_EVIDENCE and still list many AIS impressions. These are not factual/integrity inconsistencies and imply no authorship probability. We are not an opaque AI-text classifier: no scores, never "this is AI-written" / "likely AI-generated". Every finding is a named, located, itemized observation with an fp_case. For authorship detection use a dedicated tool (Pangram / GPTZero / Binoculars) — that is out of scope here, by design.

🔒 Do not wrap this skill in /loop, /schedule, or CronCreate. It is report-input — it proposes the impressions the deterministic adjudicator renders in the zero-weight AIS section. Re-firing it on a wall-clock timer adds no signal: its output changes only when the paper / ledger changes, not with the clock. Schedule the external wait that precedes it — ledger built → check once. (Mirrors ARIS's external-cadence doctrine.)

Why this exists

Real reviewers say the quiet part out loud about style: "这一看就是大模型写的味儿" (this reads like an LLM wrote it), "满篇 'it is worth noting' / '值得注意的是'" (every paragraph hedges with "it is worth noting"), "通篇 not only…but also、一堆 however/ therefore" (chains of however/therefore/moreover), "堆术语,论证是空的" (term-stuffing with no argument under it), "实验叫 'Experiment Set Gamma',从没定义" (an undefined internal codename used as if defined), "图都是一个味儿的 AI 生成图" (the figures share one generated visual grammar), "附录像把跑的 trace 一股脑倒进去" (the appendix reads like a dumped run trace). An autoresearch pipeline (or a rushed human leaning on an assistant) produces exactly these style artifacts.

These signals are real in the sense that reviewers react to them — but they are not evidence of misconduct, and not evidence of authorship. Honest LLM-assisted writing produces phrase tics; non-native English produces awkward transitions; a house style produces bold module names and consistent figures; a careful author hedges in the Limitations. So this skill's contract is narrow and permanent:

  • it emits only the 13 AIS-* style patterns, each as an impression;
  • it tags every finding not_integrity_finding: true + false_positive_risk: high and attaches an fp_case (the legitimate, not-necessarily-AI explanation);
  • it never says a paper is "AI-generated", never assigns a probability or a score, and never moves the verdict;
  • silence is the common, correct output — most papers should produce few or zero AIS impressions.

It exists so a reviewer's style reactions become named, located, checkable impressions instead of an unfalsifiable "vibe" — and so that whenever a style tell is actually a substantive problem, this skill hands it to the right integrity auditor rather than smuggling it in under a style label.

Core doctrine (the non-negotiables)

Ledger-anchored, span-verified, ZERO verdict weight, NOT an authorship classifier, reviewer ≠ adjudicator. Two passes feed the pipeline:

  1. a deterministic pass (no model) — the one objectively computable style signal: a conservative defensive-hedge density screen (AIS-DEFENSIVE-HEDGE), which fires only on a genuine pattern (≥4 distinct strong-template hedge sentences across ≥2 non-excluded sections and ≥25% of all scope sentences). It flags the recurrence of the hedge SHAPE, never who wrote it (tools/check_ai_style.py);
  2. a fresh cross-model GROSS-cases-only semantic pass — the 13 judgment-call style tells, each span-anchored, not_integrity_finding: true, false_positive_risk: high, with an fp_case. (AIS-DEFENSIVE-HEDGE is dual: deterministic for the pervasive case above + this semantic pass for the sub-threshold/qualitative posture the density screen misses.)

Both emit findings conforming to schemas/finding.schema.json (plus the AIS-only fields not_integrity_finding / fp_case). Every above-info finding cites a ledger claim_id + a verbatim span (references/integrity-forensics-contract.md rules 1–2). Because the ledger holds only checkable claims (numbers, scope, captions, citations, table cells) and almost no free prose, a style impression that cannot land on an extracted claim stays info — a note, never an above-info impression. The model proposes; tools/adjudicate_findings.py decides — and for AIS it always decides the same thing: info, zero weight, separate section (reviewer-independence.md Layer 2). This skill computes no verdict and proposes no integrity finding.

How this differs from the other auditors (route correctly)

AuditorQuestion it answersVerdict weight
ai-style-impressions (this)What AI writing-STYLE tells does a reviewer notice? Named, located, itemized impressions — NOT integrityZERO (forced to info, separate report section, excluded from overall_verdict)
presentation-signalsCheckable surface tells (dup tables, thin/LLM figures, page padding, leftover pipeline strings)labelled surface-class; minor is the honest severity for one
consistency-auditDoes the paper contradict ITSELF / described method = evaluated method?full
experiment-forensicsAre the reported numbers what the code computes? (fake GT, self-norm, phantom — L2)full
baseline-comparison-auditRight baselines present, tuned, "SOTA" earned?full
citation-forensicsDo the cited papers exist + support the claim they are used for?full
eval-design-forensicsEvaluation validity (leakage / judge bias / selective reporting)?full
proof-derivation-forensicsProof gaps / circularity / invalid steps / undefined notation?full
adversarial-case-builderStrongest evidence-bound rejection memoZERO (advisory memo)

Stay in lane. This skill emits only the 13 AIS-* style patterns — nothing else. An AIS-* impression is the lowest-stakes thing in the report by construction (it carries no verdict weight at all); never use it to carry a substantive accusation. When a style tell is actually a substantive integrity problem, hand it off (do not encode it as an AIS impression):

Pattern (AIS-*)The style impression (gross cases only)Not-necessarily-AI (high FP — the fp_case)If it is actually SUBSTANTIVE → route to
AIS-NARRATIVE-ARC-BREAKabrupt 1–2¶ intro / dump-like or vague abstract; no background→contribution→evidence arcterse-but-clear abstract; non-native phrasing; field conventionsargument chain truly breaks → HP-ARGUMENT-CHAIN-BREAK (consistency-audit)
AIS-LLM-PHRASE-TICSgeneric LLM tics overused ("it is worth noting" / "值得注意的是" / "意义在于", "not only…but also", chains of however/therefore/moreover, "therefore" mid-sentence, clichéd em-dash/semicolon, flowery empty adverbs like elegantly / theoretically)honest LLM-assisted writing; non-native English; house style (HUGE FP — gross cases only)never routes (pure style)
AIS-DEFENSIVE-HEDGE (also Step 1)pervasive "we do not claim…" / "not X but rather Y" defensive framing instead of stating what was doneone scoping sentence; Limitations hedges are expected; some venues penalize the ABSENCE of caveatsa hedge reveals a real scope/eval limitation → HP-SCOPE-INFLATE (B) / eval-design-forensics (H)
AIS-JARGON-STUFFdense term-stuffing where the surrounding argument carries no contentgenuinely dense, correct technical writing (very high FP)never routes
AIS-INVENTED-CODENAMEundefined internal-project-flavored run/experiment codename used as if defined (e.g. "Experiment Set Gamma")legitimate named methods / benchmarks / release tagspoints to a missing results file → HP-MISSING-REPRO-ARTIFACT / HP-PHANTOM-RESULT (D)
AIS-CLAUSE-FORMULA-WALLfragmented "short clause then a wall of formulas"; formulas dumped without prose connectivedense-but-correct theory; field normsa load-bearing symbol is actually UNDEFINED → HP-UNDEFINED-NOTATION (G)
AIS-GRATUITOUS-PSEUDOCODEpseudocode/algorithm blocks that merely restate the prose / add no operational contentgenuinely helpful algorithm listingsthe algorithm CONTRADICTS the described method → HP-METHOD-DRIFT (consistency-audit)
AIS-BULLET-LIST-OVERUSEprose organized as many bullets; sequential/progressive logic flattened into parallel-looking bulletslegitimate enumerations; checklistsnever routes
AIS-BOLD-MODULE-SPAMverbose module names with excessive bolding / acronym stagingreasonable emphasis; defined acronymsthe SAME module gets incompatible abbreviations → HP-ACRONYM-DRIFT (B, consistency-audit)
AIS-RESTATE-OVERCLAIMrhetorical restatement loop — repeatedly re-asserting "we propose an X / we do an X"legitimate signpostingthe claim EXCEEDS the evidence → HP-SCOPE-INFLATE (B) / family H
AIS-FOCUS-DRIFThigh-level motivation suddenly pivots to a minor implementation detail / over-emphasizes an unnecessary requirementmodular paper with explicit cross-refsthe motivation→method→experiment chain substantively breaks → HP-ARGUMENT-CHAIN-BREAK (B)
AIS-SINGLE-STYLE-FIGURESfigures share a generic generated visual grammar / single-style AI illustrationslegitimate consistent house figure style; conceptual teaserscheckable figure-vs-content thinness stays HP-LLM-FIGURE / HP-THIN-FLOAT (family F, presentation-signals)
AIS-APPENDIX-DUMPING-GROUNDappendix reads like unintegrated trace / dumping; AI-trace heavylegitimately long supplementary detailCONTRADICTS the main text → HP-APPENDIX-CONTRA (B); exact assistant/template artifact → HP-PIPELINE-ARTIFACT (F); affects reported data → family D

(The pure-style ids above migrated out of taxonomy §F into this zero-weight AIS track in v0.5; see DEPRECATED_STYLE_PATTERNS in tools/adjudicate_findings.py, which keeps the old §F ids as deprecated aliases forced to zero weight so a stale findings.json can never push them to SOFT_FLAGS.)

What this skill REFUSES to emit (even as an impression)

These are never emitted, not even as an info note. They are unfalsifiable, or they are authorship claims, or they are aesthetics — none is a located, repeated, named observation:

  • any standalone single-punctuation tell — one em-dash, one semicolon, one adverb. A style impression is about recurrence at a location, never a single character;
  • generic non-native English / awkward prose — that is a writing-quality opinion, not an AI-style tell, and is a massive FP for international authors;
  • "this is AI-written" / any authorship probability / any classifier score — out of scope by design; we audit style impressions, never provenance;
  • pure aesthetic judgments — "ugly", "too polished", "looks generated" with nothing located;
  • presence-only flags — "has bullets" / "has an appendix" / "short intro" with no located, repeated, named observation. Presence ≠ pattern.

Constants & Reviewer Calling Convention

REVIEWER_MODEL          = gpt-5.6-sol                  # different family from executor (Claude)
REVIEWER_REASONING      = xhigh                    # always; effort never lowers reviewer quality
REVIEWER_SANDBOX        = read-only                # detect-only; never mutate the paper
REVIEWER_CWD            = <paper-dir>              # so it can read claims.json + pdf-text + the PDF directly
THREAD_POLICY           = fresh mcp__codex__codex per run; NEVER mcp__codex__codex-reply
TAXONOMY_VERSION        = 0.5                      # AIS track migrated out of §F in v0.5
VERDICT_WEIGHT          = 0        # adjudicator forces every AIS finding to info + excludes it from overall_verdict
NOT_INTEGRITY_FINDING   = true     # on EVERY finding (deterministic + semantic) — not optional
DETERMINISTIC_PATTERNS  = AIS-DEFENSIVE-HEDGE   # Step 1 (tools/check_ai_style.py), pervasive case only
SEMANTIC_PATTERNS       = the 13 AIS-* style tells, GROSS cases only   # Step 2 (reviewer)
                          # AIS-DEFENSIVE-HEDGE is DUAL: density screen (Step 1) + sub-threshold (Step 2)
DEFAULT_FP_RISK         = high     # every AIS impression; this is not optional
OBS_REQUIRED            = 0        # every AIS pattern is decidable at L0 (PDF-only)
SEVERITY                = impression, never a flag (the adjudicator forces it to info; no cap needed here)
DETERMINISTIC_FINDINGS  = ai-style-impressions.deterministic.findings.json   # Step 1, ids AIS###
SEMANTIC_FINDINGS       = ai-style-impressions.findings.json                 # Step 3, ids F### (validated)
TRACE_POLICY            = forensic (never silently dropped)
TRACE_DIR               = .aris/traces/ai-style-impressions/<YYYY-MM-DD>_run<NN>/
  • Executor (Claude) builds none of the judgment: it locates the ledger + the PDF, passes paths + the ledger + the checklist to the reviewer, validates the reviewer's spans, forces the AIS fields, and writes the findings file. It never summarizes the paper, pre-judges "this looks AI-written", or leaks an opinion into the prompt (reviewer-independence.md Layer 1).
  • Reviewer (codex / gpt-5.6-sol) reads claims.json + the PDF-text + the PDF itself (visually only if it can render it; otherwise caption text only — see AIS-SINGLE-STYLE-FIGURES), proposes gross-only style impressions, and self-reports fp_case. It is the evidence-extractor, not the judge — and it never issues an authorship verdict.
  • Fresh thread per run. codex-reply is intentionally absent from allowed-tools; never carry one run's conclusions into another (the bias guard).
  • Detect-only. No Edit in allowed-tools; the reviewer sandbox is read-only. Write is used only for this skill's own findings / trace artifacts, never the audited paper. This is a third-party forensics tool, never a co-author.

Step 0 — Preconditions: locate the ledger, read the level, find the PDF

The ledger is the only structure this skill reasons over for anchoring. Resolve it, read the run's observability level L and paper_id, count the claim types AIS anchors to, and locate the PDF + text source the reviewer will read (each Bash block is self-contained — shell state does not persist between calls, so re-derive paths):

ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
# $ARGUMENTS is a paper-dir OR a claims.json path:
LEDGER="$ARGUMENTS"; [ -d "$LEDGER" ] && LEDGER="$LEDGER/claims.json"
# Only the NO-ARGUMENT case defaults to the CWD ledger. An EXPLICIT argument that
# resolves to a missing claims.json must NOT silently fall back to $(pwd) — that
# could audit the wrong paper; let the NO_LEDGER check below fire instead.
[ -z "$ARGUMENTS" ] && LEDGER="$(pwd)/claims.json"
python3 - "$LEDGER" <<'PY'
import json, sys, os
p = sys.argv[1]
if not os.path.isfile(p):
    sys.exit("NO_LEDGER: claims.json not found. Run /evidence-ledger FIRST "
             "(it writes artifact_manifest.json + claims.json).")
d = json.load(open(p, encoding="utf-8"))
cl = d.get("claims", [])
caps = [c for c in cl if c.get("type") == "caption"]
tabs = sorted({(c.get("location") or {}).get("section","") for c in cl
               if c.get("type") == "table_cell"
               and str((c.get("location") or {}).get("section","")).startswith("table:")})
scope = [c for c in cl if c.get("type") == "scope"]
print("LEDGER       =", os.path.abspath(p))
print("PAPER_DIR    =", os.path.dirname(os.path.abspath(p)) or ".")
print("PAPER_ID     =", d.get("paper_id", "?"))
print("RUN_LEVEL_L  =", d.get("observability_level", 0))
print("CLAIMS       =", len(cl))
print("SCOPE_CL     =", len(scope), "  (anchors for AIS-DEFENSIVE-HEDGE / -RESTATE-OVERCLAIM / -NARRATIVE-ARC-BREAK / -FOCUS-DRIFT)")
print("CAPTION_CL   =", len(caps), "  (anchors for AIS-SINGLE-STYLE-FIGURES / -INVENTED-CODENAME in captions)")
print("TABLE_SECS   =", len(tabs), tabs, "  (anchors for AIS-INVENTED-CODENAME used in result tables)")
paper_dir = os.path.dirname(os.path.abspath(p)) or "."
srcs = d.get("source_files", [])
for sf in srcs:
    print("SOURCE       =", sf.get("kind"), sf.get("path"))
# Deterministically pick the prose source + the PDF the reviewer will read, FROM the
# ledger's source_files (authoritative); fall back to a sorted glob. source_files paths
# may be relative to PAPER_DIR or absolute. This is the ONLY selection (no shell `ls`
# later), so the PDF-only/L0 path with no pdf source resolves to NONE.
import glob
def _resolve(rel):
    cand = rel if os.path.isabs(rel or "") else os.path.join(paper_dir, rel or "")
    return os.path.abspath(cand) if os.path.isfile(cand) else ""
def _pick(kinds, globs):
    for sf in srcs:
        if sf.get("kind") in kinds:
            r = _resolve(sf.get("path"))
            if r:
                return r
    for g in globs:
        hits = sorted(glob.glob(os.path.join(paper_dir, g)))
        if hits:
            return os.path.abspath(hits[0])
    return ""
print("PDF_TEXT_FILE=", _pick({"text", "latex"}, ["*.txt", "*.tex"])
      or "NONE (prose impressions limited to ledger spans)")
print("PDF_FILE     =", _pick({"pdf"}, ["*.pdf"])
      or "NONE (AIS-SINGLE-STYLE-FIGURES limited to caption text)")
PY

Failure / edge handling.

  • NO_LEDGER → stop; tell the user to run /evidence-ledger first. This skill never re-reads the raw PDF and invents its own structure (contract rule 1).
  • SCOPE_CL = 0 → the hedge / restatement / arc / drift anchors are absent; those impressions will correctly hold at info (common on a pure PDF-text run that parsed no scope sentences). Continue.
  • CAPTION_CL = 0 and/or PDF_FILE = NONEAIS-SINGLE-STYLE-FIGURES has no caption anchor and no image to inspect; the reviewer will almost certainly hold it at info. That is honest, not a failure.
  • CLAIMS = 0 (degenerate ledger) → every semantic impression will be unanchored → info. Run anyway; the file must exist.

Step 0 prints RUN_LEVEL_L, PAPER_ID, the absolute LEDGER / PAPER_DIR, PDF_FILE, and PDF_TEXT_FILE. Shell variables do not persist across Bash calls, so paste these literal absolute values into the <...> placeholders of each later step — do not assume an exported $LEDGER survives between blocks.

Step 1 — Deterministic impression check (no LLM): AIS-DEFENSIVE-HEDGE

The one objective, eval-testable style signal: a conservative defensive-hedge density screen, computed purely from the ledger's scope claims. It fires only on a genuine pattern — never on one scoping sentence — and flags the recurrence of the hedge SHAPE, never who wrote it. Runs before any model:

ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
LEDGER="<abs path to claims.json from Step 0>"
python3 "$ROOT/tools/check_ai_style.py" \
    --ledger "$LEDGER" \
    --out "$(dirname "$LEDGER")/ai-style-impressions.deterministic.findings.json"

This emits at most one finding with pattern_id: AIS-DEFENSIVE-HEDGE, reviewer.deterministic: true, not_integrity_finding: true, false_positive_risk: high, observability_level_required: 0, finding_id: AIS###, and ≤3 representative hedge spans, each anchored to a scope ledger claim. It fires only when ≥4 distinct strong-template hedge sentences ("we do not claim …", "not X but rather Y", "本文并不声称 …") appear across ≥2 non-excluded sections AND constitute ≥25% of all scope sentences — hedges in Limitations / Related-Work / Ethics / Broader-Impact / Acknowledgements are excluded (expected and legitimate). Because PDF-text ledgers label every section unknown, the ≥2-section gate makes this effectively LaTeX-decided — conservative by design.

Failure handling. If the tool errors, fix the invocation (python3 "$ROOT/tools/check_ai_style.py" --help) — do not hand-fabricate deterministic findings. An empty output ([]) is a valid, expected result (no pervasive hedging, or no scope claims were extracted); keep the file.

Step 2 — Cross-model GROSS-cases-only semantic pass (the 13 impressions; reviewer ≠ adjudicator)

The style tells are judgment calls. Open a fresh mcp__codex__codex thread (the Reviewer Calling Convention above), cwd = PAPER_DIR so it can read claims.json, the PDF-text, and the PDF directly. First create the forensic trace dir and fix the exact response path — shell state does not persist, so this prints the literal paths to reuse:

LEDGER="<abs path to claims.json from Step 0>"; PAPER_DIR="$(dirname "$LEDGER")"
TS="$(date +%F)"; BASE="$PAPER_DIR/.aris/traces/ai-style-impressions"
NN=1; while [ -d "$(printf '%s/%s_run%02d' "$BASE" "$TS" "$NN")" ]; do NN=$((NN+1)); done
TRACE_DIR="$(printf '%s/%s_run%02d' "$BASE" "$TS" "$NN")"; mkdir -p "$TRACE_DIR"
echo "TRACE_DIR = $TRACE_DIR"
echo "PROPOSED  = $TRACE_DIR/001-style-semantic.response.md   # save the raw reply here; reuse as PROPOSED in Step 3"

Replace the bracketed placeholders below with the real values from Step 0, send EXACTLY this, and save the verbatim reviewer reply to the PROPOSED path above (the Step 3 input) before parsing:

mcp__codex__codex:
  model: gpt-5.6-sol
  config: {"model_reasoning_effort": "xhigh"}
  sandbox: read-only
  cwd: <absolute PAPER_DIR from Step 0>
  prompt: |
    You are recording AI writing-STYLE impressions only — the kind of style tell a
    reviewer notices at a glance. You are explicitly NOT deciding whether the paper is
    AI-written, NOT assigning any probability or score, and NOT deciding whether it is
    fraudulent. You are NOT an AI-text classifier. Your output is a list of transparent,
    LOCATED style IMPRESSIONS that a deterministic adjudicator will give ZERO verdict
    weight (forced to info, excluded from the verdict, shown in a separate
    non-integrity section). It can NEVER raise a verdict. Default to SILENCE: an empty
    array [] is the expected, correct output for most papers.

    INPUTS (in your working directory, read them directly):
      - claims.json        — the evidence ledger: the authoritative, span-anchored list
        of every checkable claim {claim_id, type, text_span (VERBATIM source text),
        location, value?}. This is the ONLY thing you may anchor a finding to.
      - <PDF_TEXT_FILE from Step 0>   — extracted PDF text (for prose / phrasing / structure).
      - <PDF_FILE from Step 0, if any> — the rendered PDF (for figure-style inspection).
    RUN OBSERVABILITY LEVEL L = <L from Step 0>.

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
152
Forks
8
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
ai-style-impressions
Source
github.com/wanshuiyin/anti-autoresearch