Files
claude-code db2fba45e7
werkator build succeeded after 00:09
werkator/werkrevisor-statistics build succeeded after 00:47
werkator/release build succeeded after 00:16
werkrevisor 0.52.0: diagrams of PR Lens in the result comment (#121)
See docs/prs for the PR document.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #121
Co-authored-by: claude-code <claude-code@javagil.de>
2026-10-18 20:57:15 +02:00

152 lines
9.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Writes docs/werkrevisor-review-inside.svg and its dark variant: which files configure a review,
which services it calls, and when."""
from html import escape
LIGHT = dict(bg="#ffffff", text="#141616", muted="#55615f", soft="#3e4645", lane="#f3f6f5", card="#ffffff",
line="#c9d3d1", main="#e7f1f3", main_line="#0e6b7d", num_bg="#0e6b7d", num_text="#ffffff",
accent="#0e6b7d", chip_text="#ffffff", env="#eef1f0", warn="#fdf3e1", warn_line="#d9a441")
DARK = dict(bg="#0d1312", text="#e9efed", muted="#9fb0ad", soft="#b7c4c1", lane="#17201f", card="#1e2927",
line="#3a4a47", main="#132e35", main_line="#3aa3b8", num_bg="#3aa3b8", num_text="#0d1312",
accent="#5cc3d6", chip_text="#ffffff", env="#1a2321", warn="#2e2615", warn_line="#c99a3a")
SERVICES = {
"local": ("#0e6b7d", "werkrevisor"),
"Gitea": ("#5f6b69", "Gitea"),
"Jev": ("#7446a8", "Jev"),
"OpenRouter": ("#24733e", "OpenRouter"),
"Stats": ("#3b6fb6", "Statistics"),
}
STEPS = [
("Read the pull request", ["Gitea"], "always",
"head, WIP title, earlier result and approval, files and diff; .werkrevisor.toml"),
("Rules", ["local"], "always",
"hard paths, secrets, size, open questions, writing rules, mechanical files, images"),
("PR documents", ["Jev"], "engine classifier or combined",
"each section and scenario: clear, specific, Gherkin used well"),
("DB changesets", ["Jev"], "schema files changed, judge = jev",
"deploy risk, loss of existing data"),
("Each changed file", ["Jev"], "path judge with scope all",
"weakened checks, security, and the categories of the risk template"),
("PR document vs each file", ["Jev"], "doc check, PR document changed",
"contradiction, unrelated change, data outflow"),
("Context for the model", ["Gitea", "OpenRouter"], "full files on; excerpts for large files",
"full files at the head; an economy model selects excerpt lines"),
("Model review", ["OpenRouter"], "not in classifier mode; budget left",
"PR-Agent asks the model of the host config; it advises or decides"),
("Findings", ["Jev"], "the model reported findings",
"real defect, severity, risk category"),
("Verdict and output", ["local", "Gitea", "Stats"], "always",
"worst reason; RED for risk and finding in one file or category"),
("Diagrams", ["OpenRouter", "Gitea"], "[diagrams] enabled",
"PR Lens draws the change; SVGs on the comment, not in the verdict"),
]
CONFIGS = [
("Host config", "machine config, WERKREVISOR_CONFIG_TOML",
["model, provider order, budget", "gate, engine, Jev endpoint", "risk template, doc check"], "2–11"),
(".werkrevisor.toml", "repository, head of the default branch",
["hard and judged paths, DB schema", "PR document globs, writing rules", "Jev questions, risk template"], "1–5, 7–9"),
("Risk templates", "per ecosystem, in werkrevisor",
["jvm-spring-backend: 16 categories", "typescript-frontend: 15 categories", "tuned in .werkrevisor.toml"], "5, 9"),
("Built into werkrevisor", "code and defaults",
["questions for files and findings", "PR document check questions", "limits, timeouts, defaults"], "3–10"),
("Files of the pull request", "read at the head commit",
["PR documents, e.g. docs/prs/*.md", "AGENTS.md as context of the model", "changed files in full or as excerpt"], "3, 6–8"),
]
def svg(c):
out = []
add = out.append
add('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 900" width="1600" height="900" role="img" '
'aria-label="Inside a werkrevisor review: configuration files, steps and the services they call">')
add('<title>Inside a werkrevisor review</title>')
add(f'''<defs><style>
text {{ font-family: Inter, "IBM Plex Sans", "Segoe UI", Helvetica, Arial, sans-serif; fill: {c["text"]}; }}
.title {{ font-size: 40px; font-weight: 700; letter-spacing: -0.5px; }}
.subtitle {{ font-size: 21px; fill: {c["muted"]}; }}
.head {{ font-size: 15px; font-weight: 700; letter-spacing: 1.5px; fill: {c["muted"]}; }}
.card-title {{ font-size: 18px; font-weight: 700; }}
.card-sub {{ font-size: 13.5px; fill: {c["muted"]}; }}
.card-item {{ font-size: 14.5px; fill: {c["soft"]}; }}
.used {{ font-size: 13px; font-weight: 600; fill: {c["accent"]}; }}
.num {{ font-size: 15px; font-weight: 700; fill: {c["num_text"]}; }}
.step {{ font-size: 17.5px; font-weight: 700; }}
.what {{ font-size: 14.5px; fill: {c["soft"]}; }}
.when {{ font-size: 13.5px; fill: {c["muted"]}; font-style: italic; }}
.chip {{ font-size: 12.5px; font-weight: 700; fill: {c["chip_text"]}; letter-spacing: 0.3px; }}
.svc {{ font-size: 18px; font-weight: 700; fill: #ffffff; }}
.svc-text {{ font-size: 14px; fill: {c["soft"]}; }}
.note {{ font-size: 14.5px; fill: {c["soft"]}; }}
.footer {{ font-size: 14px; fill: {c["muted"]}; }}
</style></defs>''')
add(f'<rect width="1600" height="900" fill="{c["bg"]}"/>')
add('<text class="title" x="40" y="60">Inside a werkrevisor review</text>')
add('<text class="subtitle" x="40" y="94">Which files configure it, which services it calls, and when.</text>')
add(f'<rect x="30" y="118" width="380" height="752" rx="16" fill="{c["lane"]}"/>')
add(f'<rect x="424" y="118" width="800" height="752" rx="16" fill="{c["lane"]}"/>')
add(f'<rect x="1238" y="118" width="332" height="752" rx="16" fill="{c["lane"]}"/>')
add('<text class="head" x="50" y="148">CONFIGURATION</text>')
add('<text class="head" x="444" y="148">STEPS OF ONE REVIEW</text>')
add('<text class="head" x="1258" y="148">ENVIRONMENT</text>')
y = 164
for title, sub, items, used in CONFIGS:
h = 132
add(f'<rect x="46" y="{y}" width="348" height="{h}" rx="12" fill="{c["card"]}" stroke="{c["line"]}" stroke-width="1.5"/>')
add(f'<text class="card-title" x="64" y="{y + 28}">{escape(title)}</text>')
add(f'<text class="used" x="378" y="{y + 28}" text-anchor="end">steps {escape(used)}</text>')
add(f'<text class="card-sub" x="64" y="{y + 48}">{escape(sub)}</text>')
for i, item in enumerate(items):
add(f'<text class="card-item" x="64" y="{y + 74 + i * 20}">· {escape(item)}</text>')
y += h + 8
y = 162
row = 58
for n, (name, services, when, what) in enumerate(STEPS, 1):
add(f'<rect x="440" y="{y}" width="768" height="{row - 6}" rx="12" fill="{c["card"]}" stroke="{c["line"]}" stroke-width="1.2"/>')
add(f'<circle cx="466" cy="{y + 29}" r="15" fill="{c["num_bg"]}"/>')
add(f'<text class="num" x="466" y="{y + 34}" text-anchor="middle">{n}</text>')
add(f'<text class="step" x="494" y="{y + 24}">{escape(name)}</text>')
add(f'<text class="what" x="494" y="{y + 46}">{escape(what)}</text>')
x = 1196
for s in reversed(services):
color, label = SERVICES[s]
w = 14 + 7.6 * len(label)
x -= w
add(f'<rect x="{x:.0f}" y="{y + 9}" width="{w:.0f}" height="22" rx="11" fill="{color}"/>')
add(f'<text class="chip" x="{x + w / 2:.0f}" y="{y + 24}" text-anchor="middle">{escape(label)}</text>')
x -= 6
add(f'<text class="when" x="1196" y="{y + 47}" text-anchor="end">{escape(when)}</text>')
y += row
add(f'<rect x="440" y="{y + 4}" width="768" height="42" rx="12" fill="{c["warn"]}" stroke="{c["warn_line"]}" stroke-width="1.5"/>')
add(f'<text class="note" x="462" y="{y + 30}">A possible secret in the diff: nothing goes to Jev or a model, and the review is YELLOW.</text>')
envs = [
("werkrevisor", "Werkator", "#0e6b7d", ["starts a review for each head commit,", "its exit code is the merge gate"]),
("Gitea", "Gitea", "#5f6b69", ["reads the PR; writes the comment,", "line comments, status, approval, merge"]),
("Jev", "TypeSafe · Jev", "#7446a8", ["typed questions, probabilities;", "about 0.001 USD per review"]),
("OpenRouter", "OpenRouter", "#24733e", ["MiniMax M3: model review;", "GPT-6 Luna: excerpts; key budget"]),
("Stats", "Statistics service", "#3b6fb6", ["usage record with costs", "and seconds per phase"]),
]
y = 164
for _, label, color, lines in envs:
add(f'<rect x="1254" y="{y}" width="300" height="128" rx="12" fill="{c["env"]}" stroke="{c["line"]}" stroke-width="1.2"/>')
add(f'<rect x="1254" y="{y}" width="300" height="40" rx="12" fill="{color}"/>')
add(f'<rect x="1254" y="{y + 28}" width="300" height="12" fill="{color}"/>')
add(f'<text class="svc" x="1272" y="{y + 27}">{escape(label)}</text>')
for i, line in enumerate(lines):
add(f'<text class="svc-text" x="1272" y="{y + 66 + i * 22}">{escape(line)}</text>')
y += 140
add('<text class="footer" x="1560" y="890" text-anchor="end">git.javagil.de/mi/werkrevisor</text>')
add('</svg>')
return "\n".join(out) + "\n"
if __name__ == "__main__":
import sys
target = sys.argv[1] if len(sys.argv) > 1 else "docs"
open(f"{target}/werkrevisor-review-inside.svg", "w").write(svg(LIGHT))
open(f"{target}/werkrevisor-review-inside-dark.svg", "w").write(svg(DARK))