# AI Style Impressions

> 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; compu

- Skill: `majiayu000/ai-style-impressions` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds add majiayu000/ai-style-impressions`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/ai-style-impressions/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/ai-style-impressions

---


# AI Writing-Style Impressions — itemized, located, ZERO verdict weight (NOT integrity findings)

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)

| Auditor | Question it answers | Verdict weight |
|---------|---------------------|----------------|
| **`ai-style-impressions`** (this) | **What AI writing-STYLE tells does a reviewer notice? Named, located, itemized impressions — NOT integrity** | **ZERO (forced to `info`, separate report section, excluded from `overall_verdict`)** |
| `presentation-signals` | Checkable *surface* tells (dup tables, thin/LLM figures, page padding, leftover pipeline strings) | capped at `minor` (`SOFT_FLAGS` at most) |
| `consistency-audit` | Does the paper contradict ITSELF / described method = evaluated method? | full |
| `experiment-forensics` | Are the reported numbers what the code computes? (fake GT, self-norm, phantom — L2) | full |
| `baseline-comparison-audit` | Right baselines present, tuned, "SOTA" earned? | full |
| `citation-forensics` | Do the cited papers exist + support the claim they are used for? | full |
| `eval-design-forensics` | Evaluation validity (leakage / judge bias / selective reporting)? | full |
| `proof-derivation-forensics` | Proof gaps / circularity / invalid steps / undefined notation? | full |
| `adversarial-case-builder` | Strongest evidence-bound rejection memo | ZERO (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-BREAK` | abrupt 1–2¶ intro / dump-like or vague abstract; no background→contribution→evidence arc | terse-but-clear abstract; non-native phrasing; field conventions | argument chain truly breaks → `HP-ARGUMENT-CHAIN-BREAK` (`consistency-audit`) |
| `AIS-LLM-PHRASE-TICS` | generic 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 done | one scoping sentence; Limitations hedges are expected; **some venues penalize the ABSENCE of caveats** | a hedge reveals a real scope/eval limitation → `HP-SCOPE-INFLATE` (B) / `eval-design-forensics` (H) |
| `AIS-JARGON-STUFF` | dense term-stuffing where the surrounding argument carries no content | genuinely dense, correct technical writing (**very high FP**) | **never routes** |
| `AIS-INVENTED-CODENAME` | undefined internal-project-flavored run/experiment codename used as if defined (e.g. "Experiment Set Gamma") | legitimate named methods / benchmarks / release tags | points to a missing results file → `HP-MISSING-REPRO-ARTIFACT` / `HP-PHANTOM-RESULT` (D) |
| `AIS-CLAUSE-FORMULA-WALL` | fragmented "short clause then a wall of formulas"; formulas dumped without prose connective | dense-but-correct theory; field norms | a load-bearing symbol is actually UNDEFINED → `HP-UNDEFINED-NOTATION` (G) |
| `AIS-GRATUITOUS-PSEUDOCODE` | pseudocode/algorithm blocks that merely restate the prose / add no operational content | genuinely helpful algorithm listings | the algorithm CONTRADICTS the described method → `HP-METHOD-DRIFT` (`consistency-audit`) |
| `AIS-BULLET-LIST-OVERUSE` | prose organized as many bullets; sequential/progressive logic flattened into parallel-looking bullets | legitimate enumerations; checklists | **never routes** |
| `AIS-BOLD-MODULE-SPAM` | verbose module names with excessive bolding / acronym staging | reasonable emphasis; defined acronyms | the SAME module gets incompatible abbreviations → `HP-ACRONYM-DRIFT` (B, `consistency-audit`) |
| `AIS-RESTATE-OVERCLAIM` | rhetorical restatement loop — repeatedly re-asserting "we propose an X / we do an X" | legitimate signposting | the claim EXCEEDS the evidence → `HP-SCOPE-INFLATE` (B) / family H |
| `AIS-FOCUS-DRIFT` | high-level motivation suddenly pivots to a minor implementation detail / over-emphasizes an unnecessary requirement | modular paper with explicit cross-refs | the motivation→method→experiment chain substantively breaks → `HP-ARGUMENT-CHAIN-BREAK` (B) |
| `AIS-SINGLE-STYLE-FIGURES` | figures share a generic generated visual grammar / single-style AI illustrations | legitimate consistent house figure style; conceptual teasers | checkable figure-vs-content thinness stays `HP-LLM-FIGURE` / `HP-THIN-FLOAT` (family F, `presentation-signals`) |
| `AIS-APPENDIX-DUMPING-GROUND` | appendix reads like unintegrated trace / dumping; AI-trace heavy | legitimately long supplementary detail | CONTRADICTS 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.5                  # 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.5)** 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):

```bash
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 = NONE` → `AIS-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:

```bash
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:

```bash
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.5
  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>.

    HARD RULES (a finding that breaks any of these is worthless):
    1. GROSS ONLY. Flag only BLATANT, RECURRING cases. If you are unsure, do NOT flag.
       This is especially binding for AIS-LLM-PHRASE-TICS and AIS-JARGON-STUFF (the most
       FP-prone). A single instance is NEVER the pattern.
    2. ANCHOR. Every finding above severity "info" MUST carry >=1 evidence entry
       {claim_id, span}, where claim_id EXISTS in claims.json and span is a VERBATIM
       substring of THAT claim's text_span (no paraphrase, no added words). If you
       cannot quote a verbatim ledger span, emit it at severity "info" (a note) or drop
       it. The ledger holds numbers, scope, captions, citations, and table cells;
       generic prose is usually NOT in it, so most style impressions will correctly
       remain "info".
    3. IMPRESSION FIELDS. Set severity = "minor" for an ANCHORED impression and "info"
       for an unanchored one. Set false_positive_risk = "high",
       observability_level_required = 0, and "not_integrity_finding": true for EVERY
       finding. Provide an "fp_case": the concrete legitimate (not-necessarily-AI)
       explanation for THIS tell. NEVER use "major"/"critical" (these are impressions,
       not flags).
    4. NO ACCUSATION, NO AUTHORSHIP VERDICT, NO SCORE. description and
       recommended_reviewer_action say what a human should GLANCE AT as a readability
       impression. NEVER write "AI-generated", "AI-written", "likely AI", a probability,
       a score, "fabricated", or "reject". A style tell is a prompt to look, nothing more.
    5. STAY IN LANE. pattern_id MUST be exactly one of the 13 AIS-* below. If you notice
       a SUBSTANTIVE problem (numbers contradict, a citation looks fake, a symbol is
       undefined, the method drifts, a codename points to a missing result), do NOT
       encode it here — it belongs to the integrity auditor named in the checklist.
       Ignore it; this track is style-only and carries zero weight.
    6. NEVER EMIT (not even as info): any standalone single-punctuation tell (one
       em-dash / one semicolon / one adverb); generic non-native English / awkward prose;
       "this is AI-written" or any authorship probability/score; pure aesthetics
       ("ugly" / "too polished"); presence-only flags ("has bullets" / "has an appendix"
       / "short intro") with nothing located, repeated, and named.

    CHECKLIST (the 13 AIS-* style tells; one finding per concrete, blatant, RECURRING case.
    "ROUTE" = where it goes IF it is actually substantive — then it is NOT an AIS finding):
      AIS-NARRATIVE-ARC-BREAK  — abrupt 1-2 paragraph intro, or a dump-like / vague
                        abstract with no background -> contribution -> evidence arc.
                        fp_case: terse-but-clear abstract; non-native phrasing; field
                        conventions. ROUTE: argument chain truly breaks ->
                        HP-ARGUMENT-CHAIN-BREAK (consistency-audit).
      AIS-LLM-PHRASE-TICS — generic LLM phrasing tics OVERUSED: "it is worth noting" /
                        "值得注意的是" / "意义在于", "not only ... but also", chains of
                        however/therefore/moreover, "therefore" mid-sentence, clichéd
                        em-dash/semicolon habits, flowery empty adverbs (elegantly,
                        theoretically). fp_case: honest LLM-assisted writing; non-native
                        English; house style (HUGE FP — gross cases only). ROUTE: never
                        (pure style).
      AIS-DEFENSIVE-HEDGE — pervasive "we do not claim ..." / "not X but rather Y"
                        defensive framing instead of stating what was done. NOTE: Step 1
                        already emits this DETERMINISTICALLY for the clearly pervasive
                        case; here flag only a sub-threshold/qualitative posture it misses
                        (e.g. a self-incriminating limitation volunteered in the
                        contribution paragraph). Anchor >=2 representative hedge spans.
                        fp_case: one scoping sentence is legitimate; Limitations hedges are
                        expected; some venues penalize the ABSENCE of caveats. ROUTE: a
                        hedge reveals a real scope/eval limitation -> HP-SCOPE-INFLATE (B)
                        / eval-design-forensics (H).
      AIS-JARGON-STUFF — dense term-stuffing where the surrounding argument carries no
                        content. fp_case: genuinely dense, correct technical writing (very
                        high FP). ROUTE: never.
      AIS-INVENTED-CODENAME — an undefined internal-project-flavored run/experiment
                        codename used AS IF defined (e.g. "Experiment Set Gamma"),
                        appearing in a table/caption/heading/results sentence and never
                        defined — and not the paper's formal method name, a standard
                        dataset/split, an ablation label, or a config id. fp_case:
                        legitimate named methods / benchmarks / defined release tags.
                        ROUTE: it points to a MISSING results file / unreproducible run ->
                        HP-MISSING-REPRO-ARTIFACT / HP-PHANTOM-RESULT (family D).
      AIS-CLAUSE-FORMULA-WALL — fragmented "short clause then a wall of formulas"; formulas
                        dumped without prose connective tissue. fp_case: dense-but-correct
                        theory; field norms. ROUTE: a load-bearing symbol is actually
                        UNDEFINED -> HP-UNDEFINED-NOTATION (G).
      AIS-GRATUITOUS-PSEUDOCODE — pseudocode/algorithm blocks that merely restate the prose
                        or add no operational content. fp_case: genuinely helpful algorithm
                        listings. ROUTE: the algorithm CONTRADICTS the described method ->
                        HP-METHOD-DRIFT (consistency-audit).
      AIS-BULLET-LIST-OVERUSE — prose organized as many bullets, incl. sequential /
                        progressive logic flattened into parallel-looking bullets. fp_case:
                        legitimate enumerations; checklists. ROUTE: never.
      AIS-BOLD-MODULE-SPAM — verbose module names with excessive bolding / acronym staging.
                        fp_case: reasonable emphasis; defined acronyms. ROUTE: the SAME
                        module gets incompatible abbreviations -> HP-ACRONYM-DRIFT (B,
                        consistency-audit).
      AIS-RESTATE-OVERCLAIM — a rhetorical restatement loop: repeatedly re-asserting "we
                        propose an X / we do an X". fp_case: legitimate signposting. ROUTE:
                        the claim EXCEEDS the evidence -> HP-SCOPE-INFLATE (B) / family H.
      AIS-FOCUS-DRIFT — high-level motivation suddenly pivots to a minor implementation
                        detail, or over-emphasizes an unnecessary requirement. fp_case: a
                        modular paper with explicit cross-refs. ROUTE: the
                        motivation->method->experiment chain substantively breaks ->
                        HP-ARGUMENT-CHAIN-BREAK (B).
      AIS-SINGLE-STYLE-FIGURES — figures share a generic generated visual grammar /
                        single-style AI illustrations. If you cannot VISUALLY inspect the
                        PDF, judge only from the caption text and otherwise leave it at
                        "info". fp_case: legitimate consistent house figure style;
                        conceptual teasers. ROUTE: checkable figure-vs-content thinness
                        stays HP-LLM-FIGURE / HP-THIN-FLOAT (family F, presentation-signals).
      AIS-APPENDIX-DUMPING-GROUND — the appendix reads like unintegrated trace / dumping;
                        AI-trace heavy. fp_case: legitimately long supplementary detail.
                        ROUTE: it CONTRADICTS the main text -> HP-APPENDIX-CONTRA (B); it
                        contains an exact assistant/template artifact -> HP-PIPELINE-ARTIFACT
                        (F); it affects reported data -> family D.

    OUTPUT: a single JSON array, and NOTHING ELSE (no prose, no code fence). Each
    element conforms to schemas/finding.schema.json plus the AIS fields:
      {
        "finding_id": "F001",
        "skill": "ai-style-impressions",
        "pattern_id": "<one of the 13 AIS-* ids>",
        "title": "short, neutral",
        "description": "the located style impression, plus the explicit note that it is an impression with ZERO verdict weight — not a factual/integrity inconsistency, not evidence of AI authorship, no probability implied",
        "severity": "minor",
        "observability_level_required": 0,
        "evidence": [{"claim_id": "C0xx", "span": "verbatim substring of that claim",
                      "location": {"file": "...", "section": "..."}}],
        "verdict_local": "warn",
        "false_positive_risk": "high",
        "not_integrity_finding": true,
        "fp_case": "the concrete legitimate (not-necessarily-AI) explanation for THIS tell",
        "recommended_reviewer_action": "what to GLANCE AT as a readability impression — never 'reject', never 'AI-generated', never a score"
      }
    If nothing is blatant, return []. That is the expected output for most papers.
```

**Failure handling.**
- *MCP stall / hang* (common in long sessions): re-invoke the **identical** prompt as a
  **fresh** `mcp__codex__codex` call (gpt-5.5, xhigh) — never `codex-reply`.
- *Reviewer returns prose, not a JSON array*: the Step 3 validator extracts the
  outermost `[...]`; if there is none, re-ask once with "Output ONLY the JSON array,
  nothing else." Do not hand-author findings on the reviewer's behalf.
- *Reviewer slips toward authorship/probability language, or emits a non-AIS pattern*:
  that is what the Step 3 fields + the AIS allow-list are for — the validator strips any
  authorship verdict shape by forcing `not_integrity_finding` and drops any non-`AIS-`
  pattern. Do not pre-suppress; let the gate work.

## Step 3 — Validate + anchor + tag (keep only `AIS-*`; never cap; never let integrity through)

The executor enforces the ANCHOR gate, the AIS allow-list, and the AIS field-forcing
**before** keeping anything. Unlike `presentation-signals`, this validator does **not**
cap severity — the adjudicator zero-weights every AIS finding regardless — but it
**MUST** drop any non-`AIS-` pattern (an integrity problem must be raised by its own
auditor, never smuggled into the zero-weight track). The span must be a verbatim,
whitespace-normalized **substring of** the cited claim (`span in base`, never `base in
span` — appending hallucinated text to a real claim must fail):

```bash
ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
LEDGER="<abs path to claims.json>"
PROPOSED="<the PROPOSED path printed in Step 2>"   # the verbatim reviewer reply you saved
OUT="$(dirname "$LEDGER")/ai-style-impressions.findings.json"
python3 - "$LEDGER" "$PROPOSED" "$OUT" <<'PY'
import json, re, sys
ledger_path, proposed_path, out_path = sys.argv[1], sys.argv[2], sys.argv[3]

def nw(s):                                   # mirror adjudicator _norm_ws (whitespace only)
    return " ".join((s or "").split())

# The 13 AIS-* style impressions. The deterministic Step 1 (tools/check_ai_style.py) owns the
# PERVASIVE AIS-DEFENSIVE-HEDGE case; it is KEPT here too because that pattern is DUAL (Step 2
# catches the sub-threshold/qualitative posture the density screen misses; ids never collide —
# Step 1 is AIS###, this file is F###). EVERYTHING ELSE is DROPPED: any HP-* / integrity pattern,
# any non-AIS id, anything malformed. An integrity problem belongs to its own auditor and must
# NEVER be smuggled into the zero-weight AIS track.
AIS_PATTERNS = {
    "AIS-NARRATIVE-ARC-BREAK", "AIS-LLM-PHRASE-TICS", "AIS-DEFENSIVE-HEDGE",
    "AIS-JARGON-STUFF", "AIS-INVENTED-CODENAME", "AIS-CLAUSE-FORMULA-WALL",
    "AIS-GRATUITOUS-PSEUDOCODE", "AIS-BULLET-LIST-OVERUSE", "AIS-BOLD-MODULE-SPAM",
    "AIS-RESTATE-OVERCLAIM", "AIS-FOCUS-DRIFT", "AIS-SINGLE-STYLE-FIGURES",
    "AIS-APPENDIX-DUMPING-GROUND",
}
SEV = {"critical", "major", "minor", "info"}
VL  = {"fail", "warn", "clean", "needs_external_check"}
ABOVE_INFO = {"critical", "major", "minor"}

ledger = json.load(open(ledger_path, encoding="utf-8"))
claims = {c["claim_id"]: c for c in ledger.get("claims", []) if c.get("claim_id")}

raw = open(proposed_path, encoding="utf-8").read()
m = re.search(r"\[.*\]", raw, re.S)          # tolerate prose / code-fence wrapping
proposed = json.loads(m.group(0) if m else raw)
if isinstance(proposed, dict):               # tolerate {"findings": [...]}
    proposed = proposed.get("findings", [])

kept, dropped, demoted = [], 0, 0
n = 0
for f in proposed:
    if not isinstance(f, dict):
        dropped += 1; continue
    pid = f.get("pattern_id")
    if pid not in AIS_PATTERNS:              # keep ONLY AIS-*; never let an integrity/non-AIS pattern through
        dropped += 1; continue
    n += 1
    f["finding_id"] = f"F{n:03d}"
    f["skill"] = "ai-style-impressions"      # force-correct the skill tag
    f["pattern_id"] = pid
    f["not_integrity_finding"] = True        # the doctrine, on EVERY finding (not optional)
    f["false_positive_risk"] = "high"        # AIS impressions are high-FP by design (not optional)
    f["observability_level_required"] = 0    # every AIS pattern is L0-decidable
    if f.get("verdict_local") not in VL: f["verdict_local"] = "warn"
    f.setdefault("fp_case", "Style impression only; common in honest LLM-assisted / "
                 "non-native writing and house style — not evidence of AI authorship.")
    f.setdefault("recommended_reviewer_action", "Glance at the cited span as a readability "
                 "impression; not misconduct, not an authorship judgment, no score.")
    sev = f.get("severity")
    if sev not in SEV: sev = "minor"         # neutral default. NO severity cap: the adjudicator
                                             # forces every AIS finding to info + zero weight regardless.
    # ANCHOR gate: span must be a verbatim ws-normalized SUBSTRING of its cited claim
    anchored = []
    for ev in (f.get("evidence") or []):
        cid, span = ev.get("claim_id"), nw(ev.get("span", ""))
        c = claims.get(cid)
        if c and span and span in nw(c.get("text_span", "")):   # span IN claim, not claim IN span
            ev.setdefault("location", c.get("location", {}))     # enrich for human navigation
            ev.setdefault("artifact_hash", c.get("evidence_anchor", ""))
            anchored.append(ev)
    f["evidence"] = anchored
    if sev in ABOVE_INFO and not anchored:
        sev = "info"; demoted += 1           # unanchored impression -> info note, NEVER silently dropped
    f["severity"] = sev
    # cross-model provenance (reviewer-independence: this is a proposal, not a verdict)
    f["reviewer"] = {"model": "gpt-5.5", "reasoning": "xhigh", "deterministic": False}
    kept.append(f)

json.dump(kept, open(out_path, "w", encoding="utf-8"), indent=2, ensure_ascii=False)
print(f"validated {len(kept)} AI-style impressions "
      f"({demoted} unanchored -> info note, "
      f"{dropped} dropped: non-AIS/integrity/malformed) -> {out_path}")
PY
```

Scope of this gate: **AIS allow-list + anchoring + AIS field-forcing** (it does **not**
run `schemas/finding.schema.json`, just like `presentation-signals`) — drop everything
that is not an `AIS-*` pattern, verbatim-span anchoring (unanchored above-info →
`info`), `skill` forced to `ai-style-impressions`, `not_integrity_finding` forced
`true`, FP-risk forced `high`, observability fixed to `0`, `fp_case` /
`recommended_reviewer_action` defaulted if missing, enum coercion, and cross-model
provenance. **No severity cap** — capping is unnecessary because
`tools/adjudicate_findings.py` independently forces every AIS finding to `info` (by
`skill`, by the `AIS-` prefix, AND by the deprecated-style-id set) and assigns it
`_verdict_weight = 0`, so it is excluded from `overall_verdict` no matter what severity
it carries here.

**Worked impression — `AIS-LLM-PHRASE-TICS` (anchored → kept, but ZERO verdict weight)

…(truncated)
