Files
werkrevisor/config/hsadmin-ng.toml
claude-codeandClaude Opus 5.5 ffb999beb7 The hsadmin-ng rules name the lower-case lookup; dry runs in the PR document
A dry run with the general rules alone still gave the case finding as ORANGE.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 15:57:07 +02:00

272 lines
12 KiB
TOML
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.
# werkrevisor operator config for hsadmin-ng: larger PRs, budget 1 USD per review.
[model]
id = "openrouter/anthropic/claude-sonnet-5.5"
# Operator-maintained prices in USD per 1M tokens, from the OpenRouter models API on 2026-10-04.
input_price_usd_per_mtok = 2.00
output_price_usd_per_mtok = 10.00
# Longer prompts degrade the review even within the context window of a frontier model.
# The budget allows more: 2 attempts × (input × 2.00 + 10000 × 10.00) / 1M ≤ 1.00 USD gives 200000 tokens.
max_context_tokens = 150000
# Includes reasoning tokens.
max_output_tokens = 10000
reasoning_effort = "medium"
timeout_seconds = 240
max_retries = 1
# Anthropic counts more tokens than o200k_base; calibrate with prompt_tokens / counted_raw_tokens of the usage records.
# On werkrevisor PR#10 the factor 1.15 gave 49841 tokens, Anthropic reported 65792: 1.15 × 65792 / 49841 ≈ 1.52.
token_count_factor = 1.55
[budget]
max_usd_per_review = 1.00
min_key_remaining_usd = 2.00
# Only for the fallback estimate without a tokenizer: PR-Agent prompt plus AGENTS.md from repo_context_files.
prompt_overhead_chars = 30000
[size]
# Changed lines and files outside the docs; above them a human looks at the PR (YELLOW).
max_changed_lines = 1000
max_files = 40
[categories]
docs = ["**/*.md", "doc/**", "docs/**"]
tests = ["src/test/**"]
main = ["src/main/**"]
api_spec = ["src/main/resources/api-definition/**"]
auto_approvable = [
"docs_only", "tests_only_added", "docs_and_tests", "refactoring", "feature_with_tests", "harmless_only",
"mechanical_only", "mixed",
]
[hard_yellow]
paths = [
"**/rbac/**", "**/*Rbac*", "**/*rbac*", "**/*Auth*", "**/*auth*", "**/*Security*", "**/*Permission*",
"**/*secret*", "**/*.pem", "**/*.key", ".env*", ".tc-environment",
".gitTally*", ".werkator*", ".gitea/**", ".github/**", "Dockerfile*", "docker-compose*", "tools/remote", ".aliases",
"build.gradle*", "settings.gradle*", "gradle/**", "buildSrc/**", "gradle.properties",
"package.json", "package-lock.json", "pyproject.toml",
"src/main/resources/application*.yml", "src/main/resources/application*.yaml",
]
secret_scan = true
# Test data holds fake passwords; private keys and the real key formats stay flagged there too.
secret_scan_exclude = ["src/test/**"]
[path_judge]
# hsadmin-ng wants a human for each change of its security, CI and build configuration.
repository_may_enable = false
# The risk categories stay those of the host.
repository_may_tune_risks = false
[pr_doc]
# An open question in a changed PR document gives RED before any model call; TEMPLATE*.md and README.md never count.
globs = ["doc/PR/*.md"]
open_question_headings = ["Open Questions", "Offene Fragen"]
[review]
extra_instructions = """
Write all findings in English.
The code-review procedure in AGENTS.md is for human reviewers and does not apply to this review:
it edits no files, so write each finding as plain text without FIXME markers.
This is a Java 25 Spring Boot application with PostgreSQL, Liquibase and a ReBAC system.
Check that comments explain why, not what.
Judge whether the PR document under doc/PR/ is clear, correct and consistent with the code.
werkrevisor itself checks line length, sentences per line and sentences per paragraph of the docs;
never report these rules, not even as a style remark.
A tool writes each section of a doc from a comment `<!-- generated by … -->` up to the next heading;
report nothing about such a section.
For documentation, report only bloated or mannered prose (filler, narration of the implementation),
unclear statements, and contradictions between the PR document and the code.
Report only concrete problems with a realistic impact.
hsadmin-ng never quotes SQL identifiers, neither in its Liquibase changesets nor in the SQL that its RBAC generator writes;
PostgreSQL folds them to lower case, so a camelCase name such as tariffNos in SQL and JPA is consistent.
Report nothing about the upper or lower case of unquoted identifiers.
Code that relies on lower-case identifiers, e.g. a lookup by the lower-case column name, is correct here:
report no failure for a quoted mixed-case identifier that the PR neither adds nor refers to, not even as a missing test.
A quoted identifier that the PR adds or refers to, with a case that differs from other code, is still a finding;
so is a renamed column that other code still uses.
The user prompt contains, after the diff, the full new content of every changed file except the documentation;
documentation appears only in the diff.
Check each changed line against constraints, invariants and conventions stated elsewhere in these files,
e.g. in header comments, and report a violation as a key issue.
Review the diff; use the full files only as context.
Start every issue_header with the severity of the issue: [high], [medium] or [low].
[high]: wrong behavior, data loss, a security problem or a broken contract in a realistic scenario.
[medium]: a defect with limited impact, or a missing test for changed behavior.
[low]: cosmetics, wording, log noise or style.
[low] also, above the rules for [high] and [medium]: a finding that occurs only under a condition that the shown code rules out,
e.g. an identifier style that no file of the project uses, or an input that a check rejects;
also a missing test for such a case.
Concurrency, null values, external data or configuration are not ruled out just because no branch names them.
"""
max_findings = 3
# Without Jev, one high finding or this many medium findings give ORANGE, another medium finding YELLOW;
# with Jev, finding_real_min and finding_severity_min of the host config decide.
orange_min_medium = 2
# The complete new version of each changed file follows the diff in the prompt.
# 300000 chars are about 86000 of the 150000 input tokens and leave about 55000 for the diff of a 1000-line PR.
full_files = true
max_full_file_chars = 100000
max_full_files_chars = 300000
# PR-Agent reads AGENTS.md from the default branch, never from the PR.
repo_context_files = ["AGENTS.md"]
[writing]
# Countable rules of the writing style in AGENTS.md, checked on the changed lines of Markdown docs and PR documents.
# A violation is a hint in the comment and never blocks; the model still reviews the PR.
enabled = true
max_line_length = 128
max_sentences_per_paragraph = 5
# Empty means the docs category.
globs = []
[mechanical]
# A changed file whose every changed line is an import, a whitespace change or a rename does not go to the model.
enabled = true
# Indentation or line breaks carry meaning in these files, so a whitespace change there is never mechanical.
whitespace_significant = ["*.py", "*.yml", "*.yaml", "Makefile", "makefile", "GNUmakefile", "*.mk", "*.md",
"*.haml", "*.pug", "*.coffee", "*.nim", "*.fs"]
# Built-in import rules exist for java, kt, kts, ts, tsx, js, jsx, mjs, go and rs.
# A [mechanical.imports] table adds or replaces the line regexes of a file extension; an empty list switches it off.
[gate]
yellow_gate = "block"
human_reviewers = []
assessment_context = "ai-review/assessment"
[db_schema]
# hsadmin-ng deploys through several stages and can always restore its database,
# so only a changeset that Jev rates as risky needs a human before the merge.
judge = "jev"
[doc_check]
# Jev compares the diff of each changed file with the PR document: a contradiction, an unrelated change,
# or a change that sends customer data outside gets a line comment before a human reads the PR.
# The thresholds keep the defaults until the usage records allow a calibration on real PRs.
[engine]
# "llm": the LLM review alone; "classifier": Jev alone, no LLM; "combined": Jev, then the LLM with Jev's hints.
mode = "combined"
[classifier]
endpoint = "https://api.typesafe.ai/v1/systemone"
model = "jev-latest"
# Operator-maintained price in USD per 1M input tokens; Jev's output is free.
input_price_usd_per_mtok = 0.042
# Jev reads about 32000 tokens of state; a larger unit is not sent and makes the review YELLOW.
max_state_chars = 60000
max_units = 80
timeout_seconds = 20
pr_doc_globs = ["doc/PR/*.md"]
# Choice and score answers below this confidence do not count for the verdict; calibrate it with tools/jev-eval.
min_confidence = 0.3
# Every question asks for a problem: a value above red_above gives ORANGE, above yellow_above YELLOW.
# A score's criteria run from "no problem" to "worst problem"; its value is the normalized expected level.
[[classifier.scenario_questions]]
name = "hard_to_read"
type = "score"
instructions = """
`scenario` is a Gherkin scenario from a pull-request document; `feature` and `background` give its context.
Domain experts who do not program approve these scenarios as the specification.
How hard is the scenario to understand for such a domain expert?
The text may be German or English.
"""
criteria = [
"A domain expert understands the scenario at the first reading.",
"A domain expert understands it, but must read some steps twice.",
"A domain expert needs a developer to explain parts of it.",
"A domain expert cannot tell what the scenario requires.",
]
red_above = 0.75
yellow_above = 0.5
[[classifier.scenario_questions]]
name = "more_than_one_aspect"
type = "noul"
instructions = """
Does the Gherkin scenario in `scenario` specify more than one independent business rule or aspect,
so that it should be split into two or more scenarios?
Variants of the same rule that differ only in their data count as one aspect.
A second When with its own Then that checks a different case counts as a second aspect.
"""
red_above = 0.85
yellow_above = 0.6
[[classifier.scenario_questions]]
name = "implementation_details"
type = "noul"
instructions = """
Does the Gherkin scenario in `scenario` describe implementation details instead of business behaviour,
for example class, method, table or column names, SQL, internal HTTP headers or JSON internals?
Naming the public API endpoint, the HTTP method, the response status and the business fields
counts as observable behaviour, not as an implementation detail.
"""
red_above = 0.85
yellow_above = 0.6
[[classifier.scenario_questions]]
name = "given_when_then_misused"
type = "noul"
instructions = """
Does the Gherkin scenario in `scenario` misuse Given, When or Then?
Misuse is: an action or API call in Given, an expected result or check in When,
an action in Then, or a Then without a preceding When.
And-steps belong to the keyword above them.
"""
red_above = 0.85
yellow_above = 0.6
[[classifier.scenario_questions]]
name = "vague_then"
type = "noul"
instructions = """
Does a Then step of the Gherkin scenario in `scenario` lack a checkable outcome,
for example "works", "is handled correctly" or "behaves as expected",
without saying what an observer can check?
"""
red_above = 0.85
yellow_above = 0.6
[[classifier.section_questions]]
name = "hard_to_understand"
type = "score"
instructions = """
`section` is a section of a pull-request document that a human reviewer reads before the code.
How hard is the section to understand for a reviewer who knows the project but not this change?
Markdown lists, tables, links and code identifiers are normal here.
The text may be German or English.
"""
criteria = [
"The reviewer understands the section at the first reading.",
"The reviewer understands it, but must read some sentences twice.",
"The reviewer must guess what some sentences mean.",
"The reviewer cannot tell what the section says.",
]
red_above = 0.75
yellow_above = 0.5
[[classifier.section_questions]]
name = "slop"
type = "score"
instructions = """
How much of `section` is slop: filler words, mannered or promotional prose,
narration of the implementation instead of the reasons, or repetition of what other sentences already say?
Plain lists, tables, links and code identifiers are not slop.
The text may be German or English.
"""
criteria = [
"No slop: every sentence carries information.",
"A few filler words or one redundant sentence.",
"Several sentences are filler, mannered prose, narration or repetition.",
"The section is mostly filler, mannered prose, narration or repetition.",
]
red_above = 0.75
yellow_above = 0.5
[diagrams]
# hs-review writes them next to its draft (PR#129); the review model, Sonnet 5.5, draws them.
enabled = true
# A human reviews each of these PRs, also a GREEN one.
skip_green = false