- 註解範圍腳本的路徑少一層目錄,照著寫會找不到檔案,補上 hooks 那一層。 - 建置測試跑完卻失敗時沒有分支可走,只寫得出「通過」。現在要指出指令、結束碼,並判斷失敗是不是本次改動造成的。 - 取得差異失敗時原本會安靜跳過,現在原樣帶出錯誤並停手。判不出範圍不等於沒有東西要審。 - 禁止清單原本抄了兩份,改成只指向註解範圍的正本,兩份不會各自演化。 - 第二組不再重掃樣式判得出來的項目。hook 關掉或沒接上時就沒有最後一道網,這件事寫進技能文件,不讓它默默消失。 - 差異只算一次,落成一份快照檔給六組共讀;檔名帶執行代號,同一台機器平行跑不會互相覆蓋。註解清理改成一個檔案一個 sub agent 平行跑。
64 lines
8.7 KiB
Markdown
64 lines
8.7 KiB
Markdown
---
|
|
name: code-review
|
|
description: Review changed code against the Refactoring smell catalog in six groups (bloaters, obscurity, couplers, dispensables, comment contract, shallow modules). Run when a file change is complete or an implementation is complete, e.g. from jsc-sdlc implement. Each group runs as a sub agent over the git diff; findings are reported with file:line, severity, and refactoring, and the caller decides whether to fix. Not a replacement for the CLI's built-in security or bug review.
|
|
---
|
|
|
|
# code-review
|
|
|
|
Review changed code against `references/smells.md` (from the book *Refactoring*). The reference is written in Traditional Chinese; read it as-is.
|
|
|
|
## When to run
|
|
|
|
1. **When a file change is complete**: one file or one related group of files is done.
|
|
2. **When an implementation is complete**: all todos of a work package are done. This is the call site in `jsc-sdlc:implement` — the end of a work package, after its last todo and before it is committed and turned into a PR.
|
|
|
|
## Division of labor
|
|
|
|
- This skill covers the *Refactoring* smells, the comment contract (group 5), and shallow modules (group 6).
|
|
- Security, logic bugs, and test coverage belong to the CLI's built-in review (e.g. claude's `/security-review`); do not duplicate them.
|
|
- Swagger document auditing belongs to `jsc-review:api-doc`: response type declarations per HTTP status code, Swagger parameter descriptions, and Swagger examples down the nested data models. Group 5 here covers the source comment contract only; the two never report the same gap twice.
|
|
|
|
## Steps
|
|
|
|
1. Snapshot the review scope once, to a path that belongs to this run. In one command, expand `${TMPDIR:-/tmp}/jsc-code-review.$$.diff` into a variable, redirect `git diff` (uncommitted changes) or `git diff {base}...HEAD` (against the base branch when an implementation is complete) into it, and print the expanded path. The `$$` is what keeps two runs on one machine apart; a fixed file name lets a second run overwrite the first one's snapshot mid-review, and the six groups in step 2 would then judge bytes that were never their own diff. Read git's exit code: `0` the snapshot is written; any non-zero code means git refused (not a repository, unknown base revision, unreadable object) — report git's stderr verbatim and stop, spawn no sub agent. An empty snapshot file → delete it, report the literal 「無發現」 and stop; spawn no sub agent. The six groups in step 2 all read this one file, so git computes the diff once and every group judges the same bytes. Completion condition: the expanded snapshot path is reported as a literal path — never as the unexpanded `$$` pattern — together with a non-empty changed-file list, or the run already ended with 「無發現」 or with git's error.
|
|
2. Review in six groups, and every group **MUST run as a sub agent**; the six groups may run in parallel:
|
|
|
|
| Group | Scope |
|
|
| --- | --- |
|
|
| 1 Bloaters | smells.md group 1 |
|
|
| 2 Obscurity | smells.md group 2. For 2.5 comment scope leaks, report only what a pattern cannot decide: project code names, customer names, and context-dependent review traces. `references/comment-scope.md` is the single source of the banned and allowed lists — read it, never restate it here. Pattern-detectable items belong to `jsc-hooks/hooks/comment-scope.sh` and stay outside this group (see Notes for what that leaves uncovered) |
|
|
| 3 Couplers | smells.md group 3 |
|
|
| 4 Dispensables & Others | smells.md group 4 |
|
|
| 5 Comment contract | smells.md group 5, 5.1 to 5.8 — including 5.6 navigation links to the functions a method calls, 5.7 XML comment tag layout (opening and closing tag each on its own line), and 5.8 code-element markup by language convention (`<paramref>`, `<see cref>`, `<c>` in XML; backticks in JSDoc and docstrings) |
|
|
| 6 Shallow Module | smells.md group 6 |
|
|
|
|
Instructions for each sub agent: read only, change nothing; read the diff snapshot at the exact path step 1 reported, that one file only, and never run `git diff` again; check every changed line and its enclosing function or class against the group's definitions and detection signals in smells.md.
|
|
|
|
Each sub agent returns its findings as TSV rows — one finding per row, six fields separated by a single tab, in this order:
|
|
|
|
| Field | Content |
|
|
| --- | --- |
|
|
| 1 file | path relative to the repository root |
|
|
| 2 line | line number, a positive integer |
|
|
| 3 severity | `高`, `中`, or `低` on the smells.md scale |
|
|
| 4 group | this group's number, `1` to `6` |
|
|
| 5 item | the smell name |
|
|
| 6 detail | one sentence of evidence plus the suggested refactoring |
|
|
|
|
Fields 5 and 6 are written in Traditional Chinese. Free-form prose is not accepted: `tools/merge-findings.sh` deduplicates on fields 1 and 2, so any row off this format is rejected and its finding never reaches the report. A group with nothing to report returns the single line 「無發現」 instead of rows.
|
|
|
|
Completion condition: all six groups have returned, each with TSV rows or with 「無發現」.
|
|
3. Merge with `tools/merge-findings.sh`. Concatenate the six groups' TSV rows in group order and pipe them into the script on stdin. Start this step only once all six groups have returned; a group still running means the merge waits. Read its exit code: `0` take its stdout as the merged list — deduplicated by location and sorted by severity, 高 first and 低 last; `1` no group returned a row, so report the literal 「無發現」 and stop; `2` environment or input error, so report the script's stderr verbatim and stop, and never hand-merge as a substitute; `3` a group returned a malformed row, and the stderr names which row, so ask that one group's sub agent to re-emit in the format above and run the merge again — after a second `3`, report the malformed rows verbatim and stop. Completion condition: the merged list is on hand with every location appearing exactly once, or the run ended with 「無發現」 or with the reported error.
|
|
4. Report the finding list in Traditional Chinese, one line per merged row. **This skill never modifies code.** `jsc-hooks/hooks/write-guard.sh` in `review` mode enforces that in code: while this skill runs, it blocks write tools. Which tools those are is declared in that script's own header; never restate the list here, because a stale copy of it reads as coverage the guard does not have. Only claude has a `PreToolUse` hook, so codex, copilot, antigravity, and kiro never reach that guard — on those four this rule and the sub agent instructions are the only constraint. The caller decides what to fix (inside the implementation flow, 高 and 中 are normally mandatory, 低 is judgment).
|
|
|
|
Then close the run down in two moves. Delete the step 1 snapshot file; nothing else ever reads it, and left behind it accumulates one stale diff per review. Release the review lock by running `jsc-hooks/hooks/write-guard.sh release`, which clears `$JSC_HOME/sessions/{sid}.lastskill`. That file is how the guard recognizes the running skill, and no event tells the guard a skill ended: leave it in place and the caller's first fix — the fix this very report asked for — is blocked by the audit that just finished. State the fallback in the report either way, because an older `jsc-hooks` treats `release` as an unknown mode and exits `0` without clearing anything: the lock then lifts by itself once the file is older than `JSC_WRITE_GUARD_TTL` (900 seconds by default), and `JSC_WRITE_GUARD=off` opens it immediately.
|
|
|
|
Completion condition: the report is handed to the caller, the snapshot is deleted, the release command has been run and the wait plus the `JSC_WRITE_GUARD=off` escape hatch are stated, and the fix decision is left to them.
|
|
|
|
## Notes
|
|
|
|
- `jsc-hooks/hooks/comment-scope.sh` already matches the pattern-detectable comment scope items after every file write. Group 2 covers what patterns cannot decide — project code names, customer names, and context-dependent review traces — plus the overall judgment; the two never report the same finding twice.
|
|
- **Removed protection, on purpose.** Group 2 used to re-scan the pattern-detectable items as well, which made it the last net whenever the hook was not running: `JSC_COMMENT_SCOPE=off` turns `comment-scope.sh` off entirely, and a CLI with no `comment-scope.sh` wiring never runs it at all. That net is gone. On such a run, an issue id, a wiki page id, a work package id, or a commit hash left in a comment now reaches the commit with nothing catching it. Run `jsc-review:comment-cleanup` before commit whenever the hook is off or unwired.
|
|
- If group 5 examples are fetched from a database, they must be de-identified; never include personal data.
|
|
- When there are no findings, report the literal 「無發現」 explicitly; never leave the report empty.
|