Jev maintenance tools: shared client, spec coverage audit, voice check, nightly triage
ci / test (3.13) (push) Successful in 4m32s
ci / test (3.12) (push) Successful in 4m36s

This commit is contained in:
kemal committed 2026-10-01 13:21:36 +01:00
1 parent 0ff888e41b
commit 3ed0503bdf
9 files changed
+722 -48

No files matched your search

+152
View File
@@ -0,0 +1,152 @@
"""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())