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>
152 lines
9.0 KiB
Python
152 lines
9.0 KiB
Python
"""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))
|