/cc-quality:review — 코드 9개 항목 자동 점검#
| 항목 | 내용 |
|---|---|
| 실행 명령 | /cc-quality:review |
| 분류 | 개발 |
| 난이도 | ●●○ 보통 |
| MCP 서버 | serena, context7 |
한마디로#
작성한 코드를 PR(병합 요청)에 올리기 전, 숙련된 검토자가 9가지 기준으로 꼼꼼히 살펴보는 것과 같습니다. 출판 전 원고를 교정 보는 것처럼, 보안·성능·테스트 등 빠뜨리기 쉬운 부분을 자동으로 짚어줍니다.
누가·언제 쓰나요#
- 동료의 PR을 검토(리뷰)해야 할 때
- 코드 품질을 한번 점검하고 싶을 때
- 커밋(코드 저장)하기 전에 스스로 먼저 살펴보고 싶을 때
무엇을 해주나요#
코드를 9개 항목으로 나누어 점검하고, 별점 요약표 + 문제 목록을 만들어 줍니다.
- 9개 점검 항목: 아키텍처 / 상태 관리 / 보안 / 성능 / 테스트 / 가독성 / 다국어(i18n) / 접근성 / 구조적 결함
- 마지막 "구조적 결함" 항목은 나머지 8개와 질문이 다릅니다 — "지금 이 코드가 맞는가"가 아니라 **"나중에 누가 한 줄 빠뜨려도 아무도 모르게 되지는 않는가"**를 봅니다. 같은 버그가 두세 번 반복되는 원인을 짚는 항목입니다.
- 발견된 문제는 심각도별로 분류 — 🔴 치명적 / 🟡 개선 / 🟢 제안
- 흔한 오탐(잘못된 지적)은 자동으로 걸러줌(억제 규칙). 걸러진 항목도 따로 보여줘서 빠짐없이 확인 가능
-
점검하지 못한 항목은 "통과"로 세지 않습니다 — 도구가 안 돌았거나, MCP를 못 썼거나, 빠른 검토(
--quick)로 못 본 항목은 ✅ 대신 ⚠️ 확인 못 함(UNVERIFIED) 으로 표시되고, 하나라도 있으면 관문은 막힙니다.--allow-unverified로 넘길 수는 있지만 그 목록이 PR 본문에 남습니다 - 9개 항목은 언제나 9개 전부 판정합니다 — 여러 명이 나눠서 볼 때도 누가 어느 항목을 봤는지 표로 함께 출력하고, 아무도 맡지 않은 항목은 "확인 못 함"이 됩니다
-
--gate-mode에서는 "통과/차단" 형태의 관문(Gate) 결과표로 정리 — PR 생성을 막을 치명적 문제와 PR 본문에 넣을 참고 사항을 분리해서 보여줌. 통과는 치명적 0건 그리고 확인 못 함 0건일 때만입니다
어떻게 쓰나요#
# 특정 파일 검토
/cc-quality:review feature/auth/lib/src/presentation/bloc/login_bloc.dart
# 보안에 집중해서 검토
/cc-quality:review feature/auth/ --focus security
# 빠르게 훑어보기
/cc-quality:review . --quick
--focus: 한 항목에 집중 (예:security보안,performance성능)--quick: 빠른 검토 모드 /--verbose: 더 자세한 검토 모드--no-suppress: 오탐 걸러내기를 끄고 모든 지적을 다 보기--gate-mode: 관문(Gate) 결과표 형태로 출력 (치명적 문제와 참고 사항 분리)--allow-unverified: 확인 못 한 항목이 있어도 관문을 통과시킴 (대신 그 목록을 PR 본문에 반드시 남깁니다)
안에서 무슨 일이 벌어지나요#
-
오탐 걸러내기 준비 — 플러그인 기본 규칙과 프로젝트 자체 규칙(
.code-review-suppress.md)을 불러와 잘못된 지적을 미리 거를 준비를 합니다. - 9개 항목 점검 — 아키텍처부터 구조적 결함까지 9개 기준으로 코드를 하나씩 살펴봅니다. 항목마다 "무엇을 실제로 돌려서 그렇게 판단했는지"(명령어 출력 한 줄)를 함께 남깁니다. (필요하면 Serena로 코드 구조를, Context7로 모범 사례를 함께 확인)
-
합치기(한 번에 모아 판정) — 항목별로 나온 지적을
파일:줄 + 규칙 종류로 중복 제거하고, 오탐 걸러내기는 합친 뒤에 한 번만 적용합니다. 치명적 지적은 변경분 전체에 대해 한 번 더 확인해 살아남은 것만 관문을 막습니다 — 오탐 하나가 자동 수정 기회 2번을 태우고 파이프라인을 세우기 때문입니다. - 결과 정리 — 항목별 별점 요약표와 함께 문제를 🔴치명적·🟡개선·🟢제안으로 나눠 보여주고, 확인 못 한 항목과 걸러낸 항목도 따로 표시합니다.
⚙️ 상세 옵션·실행 명세 (개발자 / AI 에이전트용)
Triggers#
- When a PR review is requested
- When code quality inspection is needed
- For self-review before committing
Context Trigger Pattern#
/cc-quality:review {target} [--options]Parameters#
| Parameter | Required | Description | Example |
|---|---|---|---|
target | ✅ | Review target | File path, PR link |
--focus | ❌ | Focus category | architecture, state, security, performance, testing, readability, i18n, a11y, structural |
--quick | ❌ | Quick review mode | |
--verbose | ❌ | Detailed review mode | |
--no-suppress | ❌ | Disable suppression | |
--gate-mode | ❌ | Review Gate mode (separates Critical/Informational output) | |
--allow-unverified | ❌ | Do not block on ⚠️ UNVERIFIED categories. Requires listing every unverified category + reason in the PR body (durable record — console is not durable) |
Suppression List#
Automatically applies suppression rules during review to reduce false positives.
Loading Order#
- Plugin default:
cc-quality/rules/suppression.md - Project custom: project root
.code-review-suppress.md(if exists)
Application Method#
- Each review finding is compared against the suppression list
- Matching items are excluded from review results
- Can be disabled with
--no-suppressoption
Result Display#
## Suppressed (3 items excluded)
- [Readability] `*.g.dart` generated code pattern (suppression: code generation related)
- [Performance] const not applied (suppression: style/convention related)
- [Architecture] melos import path (suppression: project structure related)Review Categories#
This section is the single SoT for the category list. The count is 9, not 8. Every other statement of the list or its count is a derived view (non-normative,
plugins/cc-dev/rules/orchestration-graph.md§6) and must link here instead of restating a number. Known drift to repoint, not to copy:plugins/cc-dev/commands/run.mdStep 8.7 ("8 review categories … 3 teams", plus itsFallback (순차)line) andplugins/cc-quality/commands/feature-qa.md("8-category code review").Enumeration obligation for any split (parallel or sequential). A run that distributes categories across reviewers/teammates MUST enumerate all 9 and print the
Category → reviewermapping in the gate output (see Gate Mode Output). A category that no reviewer owns is⚠️ UNVERIFIED, never an implicit ✅ — the existing 3-teammate split drops §9 Structural Failure Modes (and §5 Testing) exactly this way, and §9 is the category that exists to catch repeat bugs. This obligation constrains the existing split; it does not add a new one.
1. Architecture#
- Clean Architecture layer separation followed
- Domain → Data → Presentation dependency direction
- Business logic accessed through UseCases
- Repository interface separation
- Feature module independence
2. State Management#
- BLoC/Cubit pattern followed
- Event → BLoC → State flow
- Immutable state via
sealed class extends Equatable+ copyWith (no@freezed) - Proper error handling
- Loading state management
3. Security#
- No hardcoded API keys or secrets
- No sensitive information in logs
- User input sanitization
- Authentication/authorization properly applied
4. Performance#
- const widgets utilized
- BlocSignalBuilder buildWhen used
- Image cacheWidth/cacheHeight applied
- Resources released in dispose
- Stream subscriptions cancelled
5. Testing#
- UseCase unit tests
- Repository tests (mocked)
- BLoC tests
- Edge cases covered
- Arrange-Act-Assert pattern
6. Readability#
- Clear and meaningful naming
- Project convention followed
- Appropriate file/class size
- Single Responsibility Principle
- Duplicate code removed
7. Internationalization (i18n)#
- All UI text uses translation keys
- context.t.* pattern used
- Proper pluralization
- Dynamic values parameterized
8. Accessibility#
- Appropriate semantic labels
- Minimum 48x48 touch target
- WCAG color contrast criteria met
9. Structural Failure Modes#
Asks "could a future change break this without anyone noticing?" rather than "is this code correct now?". Targets the repeat-bug classes static analysis misses.
- New public endpoint method reaches an authorization guard (and uses the same mechanism as its siblings)
on<Event>()handlers thatawaitspecifytransformer:deliberately- New repository mutation wired to cache invalidation
- Distinct identifier spaces / index bases / units have distinct types, not bare
int/String - New optional parameter: sibling call sites checked, with the search command recorded
- Dependency upgrade changing a default/signature: all call sites of that API audited, not just the reported one
- Raw API that has a project wrapper: wrapper used rather than the raw form
- Local widget state not duplicating a value already derivable from a source of truth
- Anything deferred as "out of scope" exists as an issue or
@Skip('#nnn')test - Generated/platform files the build needs are committed and clean-clone verified
Detailed criteria and real-case references: cc-quality/skills/code-review-checklist/REFERENCE.md §9.
Review Output Template#
Score legend (tri-state — pass / fail / undetermined, per
plugins/cc-dev/rules/orchestration-graph.md §3): ✅ = evidence quoted and green ·
⚠️ = findings, evidence present · ❌ = fail · ⚠️ UNVERIFIED = undetermined, which is
not a pass (undet:fail). The Evidence cell is mandatory for every row including rows
with no findings — "nothing found" and "nothing ran" are different results, and conflating them is
the nothing-to-check pass / silent skip fail-open pair in §3.2 of that document. This mirrors the
eval G5 convention in plugins/cc-quality/skills/code-review-checklist/SKILL.md.
## Summary
**Overall**: ⭐⭐⭐☆☆ (3/5) < !-- any UNVERIFIED row caps Overall below 4/5 -- >
| Category | Score | Evidence (what actually ran) | Key Issues |
|----------|-------|------------------------------|------------|
| Architecture | ✅ | `serena find_referencing_symbols LoginRepository` → 6 refs, layer order intact | - |
| State Management | ⚠️ | `melos run analyze` → 0 issues; 4 BlocSignalBuilder sites read | BlocSignalBuilder optimization needed |
| Security | ✅ | `grep -rn " apiKey\|secret " --include= " *.dart " lib/` → 0 hits outside Envied | - |
| Performance | ⚠️ | 3 Image widgets read in diff | Image cache size not specified |
| Testing | ❌ | `melos run test` → 118 passed, 0 failed; 0 tests for LoginUseCase | UseCase tests missing |
| Readability | ✅ | `melos run lint:check` → 0 issues | - |
| i18n | ⚠️ UNVERIFIED | Context7 unavailable — pluralization rules not checked | not judged |
| Accessibility | ⚠️ | 2 IconButton without semanticLabel | Some semantic labels missing |
| Structural Failure Modes | ⚠️ | Exhaustiveness block below (18/18 call sites) | New endpoint method has no authorization guard |
### Unverified ⚠️ (1 category — gate BLOCKED unless `--allow-unverified`)
| Category | Why undetermined | What would resolve it |
|----------|------------------|-----------------------|
| i18n | MCP (Context7) unavailable | re-run with Context7, or review pluralization manually |
### Critical Issues 🔴
1. **[Security]** API key hardcoded
- File: `lib/core/config.dart:15`
- Fix: Use environment variables via Envied
### Improvements 🟡
1. **[Performance]** Image cache size needs to be specified
- File: `lib/presentation/widget/product_card.dart:42`
- Recommended: Add `cacheWidth: 200`
### Suggestions 🟢
1. **[Readability]** Variable name improvement suggestion
- `data` → `userProfile`
### Exhaustiveness (mandatory whenever §9 flags a call-site family)
| Search command (re-runnable verbatim) | Hits | Checked | Remainder → tracked as |
|---------------------------------------|------|---------|------------------------|
| `grep -rn " allowMultiple " --include= " *.dart " .` | 18 | 18 | 0 |
| `grep -rn " unawaited( " --include= " *.dart " .` | 11 | 9 | 2 → #7746 |
### Suppressed (N items excluded)
> Items excluded by suppression rules. Use `--no-suppress` to see all.Exhaustiveness#
Mandatory block. Emit it whenever §9 flags a call-site family, a new optional parameter, or a dependency default/signature change. Four values, no prose substitutes:
| Field | Rule |
|---|---|
| Search command | The exact command, re-runnable verbatim. "audited the call sites" is not a value. |
| Hits | The command's own count. |
| Checked | How many were actually inspected. Checked < Hits with no remainder row is a defect. |
| Remainder | Every unchecked site is covered by a tracked issue id (#nnn or @Skip('#nnn')). |
- A missing Exhaustiveness block makes §9
⚠️ UNVERIFIED, not ✅ — which blocks the gate. - Rationale is §9.2's own cited failure: kobic #9162 patched 1 of 18 call sites and left the rest
unguarded, with "3 more remain" as prose in the PR body. The unit of work is the call-site family,
not the reported instance. See
plugins/cc-quality/skills/code-review-checklist/REFERENCE.md§9.2. - Loop contract for this sweep:
L-review.sibling-callsite-sweep.
Gate Mode Output (--gate-mode)#
Structured output for workflow gate when running /cc-quality:review --gate-mode:
Verdict rule (tri-state, undet:fail). Status is ✅ PASS only when
criticalCount === 0 && unverifiedCount === 0.
Status = ❌ BLOCKEDif any category is⚠️ UNVERIFIED— MCP unavailable,--quickdid not reach it, no reviewer owned it in a split, or the teammate died — unless--allow-unverified, which must list every unverified category and its reason in the PR body (durable record; console output is not durable).Critical: 0 itemsalone is not a pass. Zero findings from a category that never ran is thenothing-to-check passform; a tool/MCP absence that quietly drops a category is thesilent skipform — both inplugins/cc-dev/rules/orchestration-graph.md§3.2.- Degrading
undeterminedtowarnis permitted only for the three pre-declared cases in §3.1 of that document (nothing left to block · structurally not applicable · tooling absent), with the reason printed in theWhy undeterminedcell.
## Code Review Gate Result
**Status**: ✅ PASS / ❌ BLOCKED (PASS requires Critical 0 **and** Unverified 0)
### Category Coverage (9/9 — mapping is mandatory, also in single-reviewer runs)
| # | Category | Reviewer | Score | Evidence (what actually ran) |
|---|----------|----------|-------|------------------------------|
| 1 | Architecture | teammate-2 | ✅ | `serena find_referencing_symbols` → 6 refs |
| 2 | State Management | teammate-2 | ⚠️ | 4 BlocSignalBuilder sites read |
| 3 | Security | teammate-1 | ✅ | `grep -rn " apiKey\|secret " ` → 0 hits |
| 4 | Performance | teammate-1 | ⚠️ | 3 Image widgets read |
| 5 | Testing | lead | ⚠️ UNVERIFIED | `melos run test` not run (`--quick`) |
| 6 | Readability | teammate-3 | ✅ | `melos run lint:check` → 0 issues |
| 7 | i18n | teammate-3 | ✅ | 0 hardcoded strings in diff |
| 8 | Accessibility | teammate-3 | ⚠️ | 2 IconButton without semanticLabel |
| 9 | Structural Failure Modes | lead | ⚠️ UNVERIFIED | no owner assigned in the split |
### Unverified (Pass 0) ⚠️
> Blocks the gate. `undetermined` is not a pass.
| # | Category | Why undetermined | Durable record |
|---|----------|------------------|----------------|
| 1 | Testing | `--quick` skipped `melos run test` | PR body (required if `--allow-unverified`) |
| 2 | Structural Failure Modes | no reviewer owned it in the 3-way split | PR body (required if `--allow-unverified`) |
### Critical Issues (Pass 1) 🔴
> Issues that block PR creation
| # | Category | Issue | File | Auto-fix |
|---|----------|-------|------|----------|
| 1 | Security | API key hardcoded | config.dart:15 | ✅ Possible |
### Informational (Pass 2) 📋
> Improvement suggestions to include in PR body
| # | Category | Issue | File |
|---|----------|-------|------|
| 1 | Performance | Image cache not specified | product_card.dart:42 |
### Summary
- Categories: 9/9 judged, 2 UNVERIFIED
- Critical: 0 items (after full-diff re-check)
- Unverified: 2 categories → ❌ Gate BLOCKED (`undet:fail`) — pass `--allow-unverified` to proceed
- Informational: 3 items → Included in PR body
- Suppressed: 2 items → Excluded (applied once, after merge)
- Verdict: Critical 0 & & Unverified 2 → ❌ BLOCKEDConvergence#
Categories are reviewed as a pipeline, then merged in one barrier phase before the verdict.
That barrier is the consolidation phase this command previously lacked: splitting the review across
reviewers lowers recall precisely on cross-cutting Criticals, so the verdict is computed once, over
the merged set — never per reviewer. The substrate is unchanged: this adds a join, not a fan-out.
Notation, gate tri-state, barrier justification and loop-contract fields follow
plugins/cc-dev/rules/orchestration-graph.md (§1, §3, §4.2, §2, §5) — §4.2 already lists
"run.md Step 8.7 Critical 통합" among the confirmed-legitimate barriers, and this is that barrier's
own site.
Normative declaration of this command's flow. The step lists, parameter tables and output templates above are derived views (§6) — where they disagree, this block wins.
RV0 ACT load suppression list (plugin → project) reads:cc-quality/rules/suppression.md,.code-review-suppress.md
RV1 LOOP nine-category sweep (§1..§9) contract:L-review.nine-category-sweep
RV1.9 LOOP sibling call-site sweep (§9) contract:L-review.sibling-callsite-sweep
RV2 JOIN consolidation (dedup → suppress → recheck) mode:barrier because:a cross-cutting Critical cannot be judged per category — judge once over the merged set
RV3 GATE verdict verdict:criticalCount===0 & & unverifiedCount===0 undet:fail fail:RVfix
RV4 ACT emit report + gate tables writes:stdout,prBodyExtras.reviewFindings
RVfix LOOP caller autofix ↺ re-review contract:L-review.gate-autofix-recheck extern:true tier:standard
RV0 -- > RV1 -- > RV1.9 -- > RV2 -- > RV3 -- > RV4
RV3 ~~ > RV4 on:--allow-unverified record:Unverified table in the PR body (console is not durable)
RV3 == > RVfix on:criticalCount > 0||unverifiedCount > 0 bound:2 invalidates:RV1,RV1.9,RV2Merge Rules (RV2)#
| Rule | Value |
|---|---|
| Dedup key | file:line + rule-class. Two reviewers reporting the same key are one finding; the highest severity wins. |
| Suppression timing | Applied after the merge, never per reviewer, and counted once. The Suppressed (N) count is post-merge. |
| False-Critical guard | Every Critical must survive one re-check against the FULL diff before it blocks. A false Critical burns both autofix attempts and stops the pipeline. |
| Cross-category findings | A finding whose evidence spans two categories belongs to this phase, not to either reviewer — that is the because: of mode:barrier. |
| Unverified merge | unverifiedCount is computed here from the coverage map, so a category nobody owned surfaces at the join rather than disappearing. |
L-review.nine-category-sweep#
inv: each of §1..§9 holds exactly one summary row with BOTH Score and Evidence filled,
checked on iteration entry and exit; a missing row is UNVERIFIED, never a pass
prog: judged = categories carrying a Score, strictly increasing 0 → 9
no-prog: never re-sweep a category without new evidence — mark it UNVERIFIED and move
on instead of spending the remaining budget on the same category
term: judged === 9 (9 is defined by `## Review Categories`; not redefined here)
budget: 9 category passes, 1 each. `--focus X` = 1 deep pass + the other 8 marked UNVERIFIED
(never silently skipped); `--quick` reduces pass DEPTH, never the COUNT
exhaust: any category still unjudged → `⚠️ UNVERIFIED` and RV3 blocks; promoting it to ✅ is
prohibited
resume: the summary table is the position record — rows already holding Score+Evidence are not
re-run; re-running a category is idempotent (same `file:line + rule-class` merges at RV2)
log: one line per category, e.g. " §4 Performance: 2 findings (evidence: 3 Image widgets read) " ,
plus the UNVERIFIED list; a 0 is printed together with the evidence that produced itL-review.sibling-callsite-sweep#
Backs the Exhaustiveness block.
inv: the recorded search command is re-runnable verbatim and its own count equals the
reported Hits, on entry and exit
prog: unchecked = Hits - Checked, strictly decreasing
no-prog: never re-inspect a site without emitting a verdict for it → Rung 2
term: unchecked === 0 || every remaining site is covered by a tracked issue id
budget: 1 sweep per changed API/parameter per review; Hits > 20 → hand the family to
`plugins/cc-dev/skills/discovery-audit/SKILL.md` instead of extending this sweep
exhaust: a remainder with no tracked issue id keeps the §9 row 🔴 Critical — never downgraded to
🟡, never closed as " out of scope " in prose
resume: the previous report ' s Exhaustiveness block (command + Hits + Checked + remainder)
log: one line per sweep: " §9.2 allowMultiple → 18 hits / 18 checked / 0 left "L-review.gate-autofix-recheck#
This is the SoT for the caller's autofix loop. Callers — plugins/cc-dev/commands/run.md
Step 8.7's retry, and any auto_fix: true delegation — declare
contract:L-review.gate-autofix-recheck at their call site and inherit these values instead of
restating them. A delegated loop is still a loop (plugins/cc-dev/rules/orchestration-graph.md §2),
and an interactive AskUserQuestion is not a valid exhaust: inside a dispatched worker.
inv: every re-check re-runs the FULL nine-category sweep against the FULL diff — not only the
files the autofix touched; suppression is applied once, after the merge, and counted once
prog: blocking = criticalCount + unverifiedCount, strictly decreasing per attempt
no-prog: if the same `file:line + rule-class` is still Critical after an attempt, stop
autofixing that key and escalate (Rung 2 — `/cc-dev:unstuck`); do not spend the second
attempt on the same key
term: criticalCount === 0 & & unverifiedCount === 0
(or `--allow-unverified` with every unverified category listed in the PR body)
budget: inner 2 autofix attempts per gate entry / outer 1 re-entry per push
(how the number is chosen: `plugins/cc-dev/skills/job-timeout-budget/SKILL.md`)
exhaust: BLOCKED — block PR creation and report the surviving keys. " warn and continue " is
prohibited, as is downgrading a Critical to Informational to reach 0
resume: the last gate output ' s Critical + Unverified tables, keyed by `file:line + rule-class`;
re-running the review is idempotent
log: one line per attempt, " autofix #2: critical 3→1 unverified 0 " , plus a durable record of
what was dropped and why (fixed / suppressed / deferred with issue id)Automation Commands#
# Lint check
melos run analyze
melos run lint:check
# Test execution
melos run test
melos run test:with-html-coverage
# Formatting (kobic uses dcm format, not dart format)
melos run formatMCP Integration#
| Step | MCP Server | Purpose |
|---|---|---|
| Code analysis | Serena | Symbol and reference search |
| Pattern verification | Context7 | Best practices validation |
Examples#
File review#
/cc-quality:review feature/auth/lib/src/presentation/bloc/login_bloc.dartSecurity-focused review#
/cc-quality:review feature/auth/ --focus securityQuick review#
/cc-quality:review . --quickReferences#
- Detailed checklist:
cc-quality/skills/code-review-checklist/SKILL.md(per-category criteria:REFERENCE.md) - PR review guide:
cc-quality/commands/checklist/pr-review.md - Suppression rules:
cc-quality/rules/suppression.md - Flow notation · gate tri-state · loop contract:
plugins/cc-dev/rules/orchestration-graph.md