review-tickets
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese<!-- markdownlint-disable-file MD041 -->
STARTER_CHARACTER = 🧾
<!-- markdownlint-disable-file MD041 -->
STARTER_CHARACTER = 🧾
Review Tickets
审阅工单
Turn a review-deep, review-full, or review-diff run into ticket files that a person or a model can act on without re-reading the review. Every sentence in a ticket is a reviewer's own sentence, kept, narrowed, or deleted by a judge that read the code. The only new text is the Summary.
The input is a review-deep, review-full, or review-diff run directory. It holds , , , , , and , a clean checkout of the reviewed commit. Everything this skill writes goes under . The scripts are in next to this file and use the Python standard library, except , which runs through .
run.jsonfindings.jsonlissues.jsonlverify/results.jsonlverify/second.jsonlcheckout/<run>/tickets/scripts/dupscreen.pyuv将一次 review-deep、review-full 或 review-diff 运行转换为工单文件,使人或模型无需重读审查内容即可据此行动。工单中的每一句话都来自审阅者本人,由读过代码的 judge 决定保留、收窄或删除。唯一新增的文字是 Summary。
输入是一次 review-deep、review-full 或 review-diff 运行的目录。其中包含 、、、、 和 ,后者是被审阅 commit 的干净 checkout。此 skill 写入的所有内容都位于 下。脚本位于本文件旁边的 中,并使用 Python 标准库,只有 通过 运行。
run.jsonfindings.jsonlissues.jsonlverify/results.jsonlverify/second.jsonlcheckout/<run>/tickets/scripts/dupscreen.pyuvArguments
参数
The run directory printed by review-deep, review-full, or review-diff. Every command below uses it as . If the invocation gave no run directory, Step 1 auto-discovers it when exactly one candidate exists; it does not stop to ask.
$RUN由 review-deep、review-full 或 review-diff 打印的 run 目录。以下每条命令都将其用作 。如果调用时未提供 run 目录,且恰好存在一个候选项,Step 1 会自动发现它;不会停下来询问。
$RUNRequirements
要求
- on
claude, logged in to a provider. Each judge is aPATHprocess that inherits this environment, so model aliases such asclaude -pandopusresolve through the same provider settings as the invoking session.fable - , and the repository at
gitrun.jsonwith the reviewed commit present.repo - for the duplicate screen.
uv
run.json[alias, effort]modelsjudgecriticalsiblingconcurrency- 位于
claude中,并已登录某个提供商。每个 judge 都是一个PATH进程,会继承此环境,因此claude -p和opus等模型别名会通过与调用会话相同的提供商设置进行解析。fable - ,以及
git中run.json指向的仓库;被审阅的 commit 必须仍然存在。repo - ,用于重复项筛查。
uv
run.jsonmodels[alias, effort]concurrencyProcedure
操作流程
Run each model step as a background Bash call. The pool returns when every call has finished, and the completion notification is the signal to continue. Never poll a running step with . Run the step command itself as the background call. Never detach it with or a trailing , and never wait on a log line with . exits only when it next writes, so the call hangs until its timeout once the log gets its last line.
sleepnohup&tail -f | grep -m1tail将每个模型步骤作为后台 Bash 调用运行。调用池会在所有调用完成后返回,而完成通知就是继续执行的信号。绝不要使用 轮询正在运行的步骤。应将步骤命令本身作为后台调用运行。绝不要用 或末尾的 将其脱离,也绝不要在日志行上等待 。 只有在下一次写入时才会退出,因此日志出现最后一行后,调用会一直挂起直至超时。
sleepnohup&tail -f | grep -m1tailStep 1: Preflight and Plan
Step 1:预检与规划
bash
S="${CLAUDE_SKILL_DIR}/scripts" # replace ${CLAUDE_SKILL_DIR} with this SKILL.md's own directory on a non-Claude harness
[ -f "$S/plan.py" ] || { echo "scripts not found at $S"; exit 1; }
PF="${CLAUDE_SKILL_DIR}/../review-deep/scripts" # preflight.py has one copy, in review-deep
[ -f "$PF/preflight.py" ] || { echo "scripts not found at $PF"; exit 1; }
RUN="<user-provided run directory, or empty>"
if [ -z "$RUN" ]; then
cache="${XDG_CACHE_HOME:-$HOME/.cache}"
candidates=$(find "$cache/review-deep" "$cache/review-full" "$cache/review-diff" -mindepth 1 -maxdepth 1 -type d 2>/dev/null \
| while read -r d; do [ -f "$d/run.json" ] && echo "$d"; done)
n=$(printf '%s\n' "$candidates" | grep -c .)
if [ "$n" -eq 1 ]; then
RUN="$candidates"
echo "no run directory given, one candidate exists, using $RUN"
else
echo "no run directory given and $n candidates exist, name one explicitly:"
printf '%s\n' "$candidates"
exit 1
fi
fi
python3 "$PF/preflight.py" --tier tickets --run "$RUN" || exit 1
python3 $S/plan.py "$RUN"preflight.py --tier ticketsrun.jsongit gcinit_run.pycheckout/issues.jsonlverify/results.jsonl$SAn issue is planned when its review rating is at or above the floor, High by default, and the verifier confirmed it. Its ticket severity is the lower of the verifier's and the second check's ratings, even below the floor. can list , for issues already tracked elsewhere, and , for issues whose first verdict is unusable, such as a placeholder answer. The judge verifies included issues itself.
tickets/decisions.jsonskip_issuesinclude_issuesbash
S="${CLAUDE_SKILL_DIR}/scripts" # replace ${CLAUDE_SKILL_DIR} with this SKILL.md's own directory on a non-Claude harness
[ -f "$S/plan.py" ] || { echo "scripts not found at $S"; exit 1; }
PF="${CLAUDE_SKILL_DIR}/../review-deep/scripts" # preflight.py has one copy, in review-deep
[ -f "$PF/preflight.py" ] || { echo "scripts not found at $PF"; exit 1; }
RUN="<user-provided run directory, or empty>"
if [ -z "$RUN" ]; then
cache="${XDG_CACHE_HOME:-$HOME/.cache}"
candidates=$(find "$cache/review-deep" "$cache/review-full" "$cache/review-diff" -mindepth 1 -maxdepth 1 -type d 2>/dev/null \
| while read -r d; do [ -f "$d/run.json" ] && echo "$d"; done)
n=$(printf '%s\n' "$candidates" | grep -c .)
if [ "$n" -eq 1 ]; then
RUN="$candidates"
echo "no run directory given, one candidate exists, using $RUN"
else
echo "no run directory given and $n candidates exist, name one explicitly:"
printf '%s\n' "$candidates"
exit 1
fi
fi
python3 "$PF/preflight.py" --tier tickets --run "$RUN" || exit 1
python3 $S/plan.py "$RUN"preflight.py --tier ticketsrun.jsoncheckout/issues.jsonlverify/results.jsonlinit_run.pygit gc$S当 issue 的审查评级达到或高于门槛(默认是 High),且 verifier 已确认该 issue 时,它就会被纳入计划。其工单严重级别取 verifier 评级和 second check 评级中的较低者,即使该值低于门槛。 可以列出 ,用于已在其他地方跟踪的 issue;也可以列出 ,用于首次判定不可用的情况,例如占位回答。judge 会自行验证被纳入的 issue。
tickets/decisions.jsonskip_issuesinclude_issuesStep 2: Re-cut into units
Step 2:按文件重新拆分
bash
python3 $S/recut.py $RUNA unit is one issue's findings in one file. A themed issue that merged 77 findings across 24 files produced tickets whose cited locations did not support the claim. Per-file units fixed that, and Step 4 merges units back only when one change fixes them.
bash
python3 $S/recut.py $RUN一个 unit 是单个 issue 在单个文件中的发现。一个跨 24 个文件合并了 77 项发现的主题性 issue 曾生成工单,但工单中引用的位置并不能支持相应论断。按文件划分 unit 解决了这个问题,而 Step 4 仅在某一次更改能够解决多个 unit 时才将其重新合并。
Step 3: Judge and edit
Step 3:判断与编辑
bash
python3 $S/judge.py $RUN # every unit, background
python3 $S/apply.py $RUN
python3 $S/judge.py $RUN --critical # second model on units still Critical, background
python3 $S/apply.py $RUNThe judge reads the code, tries to refute every claim, and returns each finding as keep, edit, or delete. A keep must name the code behind each of its claims. An edit changes only what the code contradicts. Every edit and delete cites code, and a script checks each quote against git at the reviewed commit before the answer is accepted. writes . A unit is excluded when the judge refutes it, when the second model disagrees on the verdict, or when a quote is not the code.
apply.pytickets/edits.jsonjudge.py --ids FILEbash
python3 $S/judge.py $RUN # every unit, background
python3 $S/apply.py $RUN
python3 $S/judge.py $RUN --critical # second model on units still Critical, background
python3 $S/apply.py $RUNjudge 会读取代码,尝试反驳每项论断,并将每项发现返回为 keep、edit 或 delete。每条 keep 都必须指出其各项论断背后的代码。edit 只修改代码与之矛盾的部分。每次 edit 和 delete 都会引用代码;在接受回答前,脚本会将每段引文与 git 中被审阅 commit 的代码进行核对。 会写入 。当 judge 驳回某个 unit、第二个模型不同意其判定,或某段引文与代码不符时,该 unit 会被排除。
apply.pytickets/edits.jsonjudge.py --ids FILEStep 4: Merge siblings that share one fix
Step 4:合并共享同一修复的同级 unit
bash
python3 $S/siblings.py $RUN # background
python3 $S/judge.py $RUN --merged # background
python3 $S/apply.py $RUN --mergedSplitting by file also splits one defect seen from a caller and its callee. For every issue with two or more ready units, a judge groups the units whose defect one change at a single location resolves. The same pattern in two independent places stays two tickets. A merged ticket is then judged again as a whole, which checks its new Summary and can edit its findings. A merged ticket keeps every finding as its own bullet, so a caller and a callee both appear.
bash
python3 $S/siblings.py $RUN # background
python3 $S/judge.py $RUN --merged # background
python3 $S/apply.py $RUN --merged按文件拆分也会把一个从调用方及其被调用方观察到的缺陷拆开。对于每个包含两个或更多 ready unit 的 issue,judge 会对 unit 分组:只有当同一位置的一次更改能够解决这些 unit 的缺陷时,才将它们归为一组。相同模式出现在两个独立位置时,仍保留为两个工单。合并后的工单会再次作为整体接受 judge 检查,这会检查其新的 Summary,并可编辑其 findings。合并后的工单仍将每项发现保留为独立的项目符号,因此调用方和被调用方都会出现。
Step 5: Assemble and check
Step 5:组装与检查
bash
python3 $S/assemble.py $RUN
python3 $S/checks.py $RUNassemble.pytickets.mdreport.mdchecks.pyproblems 0bash
python3 $S/assemble.py $RUN
python3 $S/checks.py $RUNassemble.pytickets.mdreport.md在发布任何内容之前, 必须打印 。它会将每个代码块与 git 逐字节比对,检查每个位置,并标记 Jira 无法渲染的标记语法。
checks.pyproblems 0Step 6: Screen for duplicates
Step 6:筛查重复项
Export the target project's issues with any Jira client as JSON, one object per issue with , , and , at the top level or under . Then:
keysummarydescriptionfieldsbash
uv run --with scikit-learn --with numpy python $S/dupscreen.py $RUN existing.jsonThe screen prints pairs by shared vocabulary for a person to read. Most pairs share a topic, not a defect.
使用任意 Jira 客户端将目标项目的 issues 导出为 JSON,每个 issue 一个对象,包含 、 和 ,这些字段可以位于顶层或 下。然后运行:
keysummarydescriptionfieldsbash
uv run --with scikit-learn --with numpy python $S/dupscreen.py $RUN existing.json该筛查会按共享词汇打印配对,供人工阅读。大多数配对共享的是主题,而不是缺陷。
Step 7: Record decisions
Step 7:记录决定
Write the operator's decisions to and rerun Step 5.
tickets/decisions.json| Key | Effect |
|---|---|
| |
| A list such as |
| Read by Step 1. |
将操作者的决定写入 ,然后重跑 Step 5。
tickets/decisions.json| 键 | 效果 |
|---|---|
| |
| 诸如 |
| 由 Step 1 读取。 |
Step 8: Post
Step 8:发布
Post only after the operator's explicit go. Use whichever Jira client is available. The rules below held on 385 tickets.
- Post one ticket at a time, in priority order. Map Critical to Highest, High to High, Medium to Medium, and Low to Low. Send the , the
summary, the mapped priority, thetype, and thelabelsas Jira wiki markup.description - Append the ticket ID and the new key to a log the moment the create call returns, before anything else.
- Read every ticket back with its description as ADF and run . Stop on the first mismatch. One run stopped when two code spans joined by an en dash rendered as a single span. The checker caught it before the next 300 tickets repeated it.
python3 $S/readback.py TICKET_JSON FIELDS_JSON - Stop on the first error. Retry only a 429, after its Retry-After wait. A timeout or a 5xx may have created the issue, so reconcile before retrying.
- Before any resume, reconcile the log against a search for the run's ticket label (,
review-deep, orreview-full, read fromreview-diff). Jira search leaves out archived issues, so an issue archived on purpose never counts as a stray.run.json - Post the first ticket alone, read it back field by field, and continue only after it matches.
只有在操作者明确同意后才能发布。使用任意可用的 Jira 客户端。以下规则已在 385 个工单上得到验证。
- 每次只发布一个工单,并按优先级排序。将 Critical 映射为 Highest,High 映射为 High,Medium 映射为 Medium,Low 映射为 Low。以 Jira wiki 标记发送 、
summary、映射后的优先级、type和labels。description - 在 create 调用返回的当下,先将工单 ID 和新 key 追加到日志,然后才进行其他操作。
- 将每个工单连同其 ADF 格式的 description 读回,并运行 。遇到第一个不匹配时立即停止。有一次运行中,两个由 en dash 连接的代码 span 被渲染成一个 span,导致运行中止。检查器在后续 300 个工单重复此问题之前便捕获了它。
python3 $S/readback.py TICKET_JSON FIELDS_JSON - 遇到第一个错误时立即停止。仅在收到 429 后按照 等待再重试。超时或 5xx 可能已经创建了 issue,因此重试前必须先进行核对和协调。
Retry-After - 恢复运行前,将日志与针对该 run 工单标签的搜索结果进行协调核对;标签从 中读取,值为
run.json、review-deep或review-full。Jira 搜索不会返回已归档的 issue,因此有意归档的 issue 不会被视为意外创建的工单。review-diff - 先单独发布第一个工单,逐字段读回,只有完全匹配后才继续。
Step 9: Bundle deferred tickets
Step 9:打包延期工单
bash
python3 $S/bundle.py $RUNtickets/bundle/ticket.jsontickets/bundle/<name>-deferred-defects.mdbash
python3 $S/bundle.py $RUNtickets/bundle/ticket.jsontickets/bundle/<name>-deferred-defects.mdJira wiki markup
Jira wiki 标记
The assembler handles these, and flags any it misses. Each was found by posting and reading back.
checks.py- Inside , Jira drops the escaping backslash before
{{...}}and keeps it before anything else, so{ } [ ] * | !shows{{"a\"b"}}."a\"b" - A code span holding only a backslash has no working encoding. loses the monospace,
{{\}}shows two backslashes, and{{\\}}stays undecoded. The assembler writes the word backslash.\ - Jira closes only next to certain characters. The confirmed ones are a space,
{{...}}, and a newline after a span, and a space,, / . ' - ; ),/, and a newline before one. The assembler puts a space between a span and any other neighbor.( - A markdown backtick run pairs only with a run of the same length. A literal in a finding stays text.
```json - Code languages outside are written as
javascript json yaml bash python sql csharp, because no other language name was seen rendering.none
组装器会处理以下情况, 会标记任何遗漏。每一项情况都是通过发布并读回发现的。
checks.py- 在 内部,Jira 会丢弃位于
{{...}}前的转义反斜杠,但会保留其他字符前的反斜杠,因此{ } [ ] * | !会显示为{{"a\"b"}}。"a\"b" - 仅包含一个反斜杠的代码 span 没有可行的编码方式。会丢失等宽格式,
{{\}}会显示两个反斜杠,而{{\\}}不会被解码。组装器会写入单词 backslash。\ - Jira 只有在紧邻某些字符时才会关闭 。已确认的情况是:span 后面为空格、
{{...}}或换行,或者 span 前面为空格、, / . ' - ; )、/或换行。组装器会在 span 与任何相邻内容之间放入一个空格。( - Markdown 中连续的 backtick 只有长度相同时才会配对。finding 中原样出现的 会保持为文本。
```json - 之外的代码语言会写为
javascript json yaml bash python sql csharp,因为没有观察到其他语言名称能够正常渲染。none
What the settings are based on
这些设置的依据
| Setting | Evidence |
|---|---|
| Opus at xhigh as the judge | On 10 units holding 10 known wrong sentences, Opus xhigh removed 10 of 10 for $3.84, Fable at medium 10 of 10 for $6.93, Fable at high 10 of 10 for $9.65, and Opus at high 9 of 10 for $1.72. On the full run, 618 units took 17 minutes and $188.59 at list price with 50 concurrent. |
| A reason on every keep | Every wrong sentence that survived a judge was a keep with an empty reason. A re-judge of the 291 units with such keeps, under the rule, cost $98.49. |
Isolated | Workflow subagents received the operator's CLAUDE.md, skill listing, MCP instructions, and hooks. A Serena hook denied their reads, and the CLAUDE.md prose rules changed punctuation inside quoted code. A bare process starts with about 3,000 tokens of context, against 77,000. |
| |
| A 5-minute prompt cache | With |
| A content check on every answer | A JSON schema checks shape. Models answered schema rejections with placeholders such as |
| One process per ticket, not one long session | A session judging many tickets rereads its whole growing context every turn. A per-ticket session ends near 26,000 tokens. |
| A clean checkout of the commit | Judges that searched the live tree read other reviewers' findings from the review state directory. The checkout also drops the files listed in |
| The lower of two ratings | The operator's rule. Judges rate lower than reviewers, and on one 618-unit production run 459 units moved down. |
| 设置 | 依据 |
|---|---|
| 将 Opus at xhigh 用作 judge | 在包含 10 个已知错误句子的 10 个 unit 上,Opus xhigh 以 $3.84 删除了 10/10,Fable at medium 以 $6.93 删除了 10/10,Fable at high 以 $9.65 删除了 10/10,而 Opus at high 以 $1.72 删除了 9/10。在完整运行中,以 50 并发处理 618 个 unit 耗时 17 分钟,按标价花费 $188.59。 |
| 每条 keep 都必须有 reason | 每个在 judge 审查后仍被保留的错误句子,都是一条 reason 为空的 keep。对 291 个包含此类 keep 的 unit 按该规则重新 judge,花费了 $98.49。 |
隔离的 | 工作流 subagent 会收到操作者的 CLAUDE.md、skill 清单、MCP 指令和 hooks。Serena hook 拒绝读取,而 CLAUDE.md 中的散文规则改动了引用代码里的标点。bare 进程启动时约有 3,000 tokens 上下文,而原会话为 77,000。 |
在 | |
| 5 分钟 prompt 缓存 | 设置 |
| 对每个回答执行内容检查 | JSON schema 会检查结构。模型在 schema 拒绝后给出了 |
| 每个工单使用一个进程,而不是一个长会话 | 判断许多工单的会话每轮都会重读不断增长的整个上下文;每个工单单独的会话结束在约 26,000 tokens。 |
| 使用被审阅 commit 的干净 checkout | 搜索实时工作树的 judge 会从 review state 目录中读到其他审阅者的 findings。checkout 还会移除 |
| 取两个评级中的较低者 | 这是操作者制定的规则。judge 给出的评级通常低于审阅者;在一次包含 618 个 unit 的生产运行中,有 459 个 unit 的评级被下调。 |
Reporting counts
计数报告
A ticket carries one category label for every perspective that raised any of its findings, and most tickets carry more than one. In a table of tickets by category and priority, the category rows count labels, so they add up to more than the number of tickets. Label the rows as tickets carrying that category, and give distinct tickets as a separate total.
每个提出过任意 finding 的视角都会在工单上获得一个 category 标签,而且大多数工单不止一个。在按 category 和优先级统计工单的表格中,category 行统计的是标签数,因此总和会超过工单总数。应将这些行标为“携带该 category 的工单”,并将 distinct 工单数作为单独总计。
Files
文件
Under :
$RUN/tickets/- ,
plan.json, the planned issues and their per-file unitsunits.jsonl - ,
judge/results.json, every judge answer with its attempts and cost, andjudge/merged.jsonwith each process's event streamjudge/calls/ - ,
edits.json,merges.json,merge_edits.jsondecisions.json - , one posting payload per ticket,
final/<ID>.json, andfinal/manifest.jsonpostable.json - ,
dup_pairs.jsonbundle/
In the run directory itself, beside :
report.md- , every ticket as plain markdown behind a perspective by priority table
tickets.md
位于 下:
$RUN/tickets/- 、
plan.json,包含已规划的问题及其按文件划分的 unitunits.jsonl - 、
judge/results.json,包含每次 judge 回答及其尝试次数和成本;judge/merged.json包含每个进程的事件流judge/calls/ - 、
edits.json、merges.json、merge_edits.jsondecisions.json - ,每个工单一个发布载荷;还包括
final/<ID>.json和final/manifest.jsonpostable.json - 、
dup_pairs.jsonbundle/
在 run 目录本身中、 旁边:
report.md- ,在按视角和优先级排列的表格之后,以纯 Markdown 列出每个工单
tickets.md
Tests
测试
Tests and evals for review-deep and review-tickets, plus the guard tests and preflight tests that cover all four skills, live in at the root of this plugin's repository, outside the installed skill.
evals/| Check | Command, from | Cost |
|---|---|---|
| Unit tests, one directory per skill, plus preflight and guard | | none |
| End-to-end replay on the harbor fixture, all three kinds | | none |
Live quality eval against | | about $3 at list price |
Run the unit tests and the replay after any change to . The replay stops with "prompt changed" when a prompt changes. Record again with , check the run with , and save it with . Run the live eval on demand after changing a model, an effort level, or a prompt.
scripts/python3 pipeline.py WORK_DIR --mode recordlive_eval.pypython3 golden.py save RUN_DIRreview-deep 和 review-tickets 的测试与评估,以及覆盖全部四个 skill 的 guard 测试和 preflight 测试,都位于此插件仓库根目录的 中,在已安装 skill 之外。
evals/| 检查项 | 命令,从 | 成本 |
|---|---|---|
| 单元测试,每个 skill 一个目录,外加 preflight 和 guard | | 无 |
| 对 harbor fixture 执行三种类型的端到端回放 | | 无 |
- 针对 的实时质量评估 |
fixture/truth.json| 按标价约 $3 |python3 live_eval.py WORK_DIR --run
修改 中的任何内容后,都要运行单元测试和回放。prompt 发生变化时,回放会以“prompt changed”停止。使用 重新录制,使用 检查该 run,并使用 保存。修改模型、effort 级别或 prompt 后,可按需运行实时评估。
scripts/python3 pipeline.py WORK_DIR --mode recordlive_eval.pypython3 golden.py save RUN_DIRRules
规则
- NEVER post without the operator's explicit go, and never bulk-post before the first ticket has been read back.
- NEVER let a judge read the live working tree. Judges run in
run.json.checkout - NEVER accept a model answer without its content check, and never accept an evidence quote that is not the code at the reviewed commit.
- Keep prompts in as
prompts/. A markdown formatter rewrote a.txtprompt and changed its meaning..md - Report counts as measured. A ticket count, a label count, and a finding count are three different numbers.
- 未经操作者明确同意,绝不发布;在第一个工单完成读回验证前,绝不批量发布。
- 绝不让 judge 读取实时工作树。judge 在 的
run.json中运行。checkout - 绝不在没有内容检查的情况下接受模型回答,也绝不接受与被审阅 commit 处代码不符的证据引文。
- 将 prompt 保留在 下并使用
prompts/后缀。Markdown formatter 曾重写.txtprompt 并改变其含义。.md - 按实际测得结果报告。工单数、标签数和 finding 数是三个不同的数字。