"""Check public texts against the brand voice in branding/README.md: plain, exact, every claim next to its limitation. Code checks the mechanical rules (em and en dashes in prose, banned marketing words). For every paragraph of at least 15 words, Jev answers three questions in one request: does the paragraph claim something about SimurgArray without any limitation or condition (Noul), does it use promotional exaggeration (Noul), and how plain is the tone (Score, four concrete levels). The tool marks paragraphs to look at; it never rewrites them. python scripts/jev/voice_check.py README.md blog/*.md my_post.txt # writes voice_report.md python scripts/jev/voice_check.py --rules-only blog/*.md # mechanical rules only, no API calls """ from __future__ import annotations import argparse import re import sys from dataclasses import dataclass, field from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent)) from client import ROOT, noul, score, system_one BANNED = ["revolutionary", "cutting edge", "cutting-edge", "ai powered", "ai-powered", "next generation", "next-generation", "seamless", "seamlessly", "unlock", "game changer", "game-changing", "best in class", "world class", "blazing"] # fmt: skip LIMIT_T, HYPE_T, TONE_T = 0.7, 0.6, 1.5 TONE_LEVELS = [ "Plain and exact: short sentences, concrete facts or numbers, no salesmanship", "Mostly plain, with an occasional vague or enthusiastic phrase", "Noticeably promotional or vague: several adjectives or benefits stated without specifics", "Marketing copy: superlatives, buzzwords and promises dominate", ] @dataclass class Para: path: str line: int heading: str text: str issues: list[str] = field(default_factory=list) def paragraphs(path: Path, text: str) -> list[Para]: """Prose paragraphs and bullets; code blocks, tables, headings and front matter lines are skipped.""" out: list[Para] = [] heading, buf, start, in_code = "", [], 0, False def flush() -> None: if buf: out.append(Para(str(path), start, heading, " ".join(buf))) buf.clear() for i, raw in enumerate([*text.splitlines(), ""], 1): ln = raw.rstrip() if ln.lstrip().startswith("```"): flush() in_code = not in_code continue if in_code: continue if not ln.strip() or ln.lstrip().startswith("|") or ln.startswith("#"): flush() if ln.startswith("#"): heading = ln.lstrip("#").strip() continue m = re.match(r"\s*([*>-]|\d+\.)\s+", ln) if m: flush() ln = ln[m.end() :] if ln.lstrip().startswith("<"): # raw HTML blocks (banners, badges) are layout, not prose continue if not buf: start = i buf.append(ln.strip()) return out def mechanical(p: Para) -> list[str]: prose = re.sub(r"`[^`]*`|https?://\S+|\([^)]*\.(md|png|jpg|svg)\)", "", p.text) prose = re.sub(r"\"[^\"]*\"|\u201c[^\u201d]*\u201d", "", prose) # quoted words are examples, not usage if re.match(r"\s*(Never|No|Do not)\b", prose): prose = "" issues = [] if re.search("[—–]", prose): issues.append("em or en dash in prose") low = prose.lower() found = [w for w in BANNED if re.search(rf"\b{re.escape(w)}\b", low)] if found: issues.append("banned words: " + ", ".join(found)) return issues QUESTIONS = { "no_limit": noul( "Does `paragraph` claim that SimurgArray achieves, guarantees or provides something (accuracy, speed, " "compatibility, safety, a benefit) without stating any limitation, condition, number or caveat in the same " "paragraph?", true="A capability or benefit is asserted with nothing that bounds it", false="There is no such claim, or the claim comes with a number, condition or limitation", ), "hype": noul( "Does `paragraph` use promotional exaggeration: superlatives, buzzwords or unqualified promises instead of " "plain description?", true="Contains marketing language or overstatement", false="Plain description, even if enthusiastic in a modest way", ), "tone": score("How plain and exact is the tone of `paragraph`?", TONE_LEVELS), } def judge(p: Para, doc: str) -> dict[str, float]: ans = system_one({"document": doc, "section": p.heading, "paragraph": p.text}, QUESTIONS) return {"no_limit": float(ans["no_limit"]["noul"]), "hype": float(ans["hype"]["noul"]), "tone": float(ans["tone"]["score"])} # fmt: skip def main(argv: list[str] | None = None) -> int: ap = argparse.ArgumentParser(description=__doc__.split("\n\n")[0]) ap.add_argument("files", nargs="+", type=Path) ap.add_argument("--out", type=Path, default=ROOT / "voice_report.md") ap.add_argument("--rules-only", action="store_true", help="mechanical rules only, no API calls") a = ap.parse_args(argv) flagged: list[tuple[Para, dict[str, float]]] = [] n = 0 for f in a.files: doc = f.name for p in paragraphs(f, f.read_text(encoding="utf-8")): p.issues = mechanical(p) vals: dict[str, float] = {} if not a.rules_only and len(p.text.split()) >= 15: n += 1 vals = judge(p, doc) if vals["no_limit"] >= LIMIT_T: p.issues.append(f"claim without a limitation (p {vals['no_limit']:.2f})") if vals["hype"] >= HYPE_T: p.issues.append(f"promotional language (p {vals['hype']:.2f})") if vals["tone"] >= TONE_T: p.issues.append(f"tone {vals['tone']:.1f} of 3") if p.issues: flagged.append((p, vals)) lines = ["# Voice check", "", f"{n} paragraphs judged, {len(flagged)} flagged.", ""] for p, _ in flagged: lines.append(f"* `{p.path}:{p.line}` ({p.heading}) **{'; '.join(p.issues)}**") lines.append(f" > {p.text[:400]}") a.out.write_text("\n".join(lines) + "\n") print(f"{n} paragraphs judged, {len(flagged)} flagged → {a.out}") return 0 if __name__ == "__main__": sys.exit(main())